ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스터디 11주차] Enum
    프로그래밍 언어/JAVA 2021. 7. 10. 22:03
    더보기

    목표: 자바의 열거형에 대해 학습

     

     

     

    1. enum 정의하는 방법

    먼저, enum이 무엇인지부터 알아보자.

    enum enumerated type의 줄임말로 서로 관련된 상수를 편리하게 선언하기 위한 것으로 상수를 여러 개 정의할 때 사용한다.

    기존에 상수를 사용하면서 발생했던 문제를 개선하고자 JDK 1.5부터 추가된 기능으로 타입까지 비교가 가능하다.

     

    그러면 이러한 enum은 왜 만들어졌을까?

    enum을 사용하지 않고 상수를 관리하는 코드를 봐보자.

    아래의 코드는 enum 없이 기존의 public static final을 이용하여 상수를 선언하는 것이다.

    public class NoEnum {
    	public static final int KangBaekHo = 10;
    	public static final int ChaeChiSu = 4;
    	public static final int SeoTaeUng = 11;
    
    	public static void main(String[] args) {
    		int player = KangBaekHo;
    		switch (player) {
    			case KangBaekHo:
    				System.out.println("북산고 강백호입니다.");
    				break;
    			case ChaeChiSu:
    				System.out.println("북산고 채치수입니다.");
    				break;
    			case SeoTaeUng:
    				System.out.println("북산고 서태웅입니다.");
    				break;
    		}
    
    	}
    
    }

    이렇게 코드를 짜서 상수를 관리할 수 있지만 효율적이지 않은 점은 각각의 상수에 부여된 10, 4, 11이라는 리터럴은 단순히 상수에게 초기값을 주기위한 수단으로 사용되고 논리적으로 아무런 의미가 없다는 것이다.

    즉, KangBaekHo 상수에 초기화된 정수 10은 아무런 관련이 없고 굳이 10이여야 할 이유가 없다.

     

    다른 enum 없이 상수를 관리했을 때 문제점을 코드를 보며 살펴보자.

    public class NoEnum2 {
    	/*농구선수에 관한 상수*/
    	public static final int KangBaekHo = 10;
    	public static final int ChaeChiSu = 4;
    	public static final int SeoTaeUng = 11;
    	
    	/*야구선수에 관한 상수*/
    	public static final int KangBaekHo = 1;
    	public static final int LeeDaeHo = 2;
    	public static final int CooSinSoo = 3;
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    
    	}
    
    }
    

    위 코드에서 농구선수 강백호와 야구선수 강백호는 동명이인이지만 다른 사람을 나타내는 상수이다. 하지만 위 코드처럼 선언하면 상수명이 중복되기 때문에 에러(Exception in thread "main" java.lang.Error: Unresolved compilation problem)가 발생한다.

    이러한 문제점을 해결하기 위해서는 상수명을 다르게 하기 위해 의도치 않게 코드가 길어지는 단점이 존재하게 된다.

    public class NoEnum2 {
    	/*농구선수에 관한 상수*/
    	public static final int Baksetball_KangBaekHo = 10;
    	public static final int Baksetball_ChaeChiSu = 4;
    	public static final int Baksetball_SeoTaeUng = 11;
    	
    	/*야구선수에 관한 상수*/
    	public static final int Baseball_KangBaekHo = 1;
    	public static final int Baseball_LeeDaeHo = 2;
    	public static final int Baseball_CooSinSoo = 3;
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    
    	}
    
    }
    

    이렇게 기존의 상수를 관리하는 것에는 문제점과 비효율적인 면들이 존재해 enum이라는 개념을 도입하게 된 것이다.

     

     

    그러면 enum을 정의하는 방법은 어떻게 될까?

    enum을 정의하는 방법은 아래와 같다.

     

    enum 열거형이름 {상수명1, 상수명2, ...}

     

    또는, 값을 가지고 있는 상수를 가지고 있는 enum을 선언할 수도 있다.

    값(필드)를 추가하는 방법은 추가하고 싶은 값을 정의하고 생성자의 인자로 추가한 뒤 각각의 상수에 값을 입력하면 된다.

    public enum BasketballPlayers {
    	KANGBAEKHO(10), CHAECHISU(4), SEOTAEUNG(11);
    	
    	private int value;
    	BasketballPlayers (int value) {
    		this.value = value;
    	}
    
    }
    

    여기서 알아두어야 할 점은 enum의 생성자는 접근제어자가 묵시적으로 private이기 때문에 외부에서는 enum의 생성자를 호출할 수 없다는 것이다.

     

     

    자바에서의 enum에 포함된 상수들은 단순한 기본형이 아닌 해당 enum type의 객체이다.

    또한, enum의 멤버 중 하나를 호출하면 열거된 모든 상수의 객체가 생성된다는 것이 enum의 특징 중 하나이다.

    아래의 코드 실행 결과를 보면 KANGBAEKHO 하나를 호출해도 열거된 모든 상수의 생성자가 호출되었음을 확인할 수 있다.

    public enum BasketballPlayers {
    	KANGBAEKHO(10), CHAECHISU(4), SEOTAEUNG(11);
    	
    	private int value;
    	BasketballPlayers (int value) {
    		this.value = value;
    		System.out.println("생성자" + this.value);
    	}
    
    }
    

     

     

    또 다른 enum의 특징으로는 추상 메서드를 선언해서 각 상수 별로 다르게 동작하는 메소드를 구현할 수 있다는 것이다.

    public enum BasketballPlayers {
    	KANGBAEKHO(10) {
    		@Override
    		int Point(int runningTime) {
    			return runningTime * STAEMINA * 5;
    		}
    	}, 
    	
    	CHAECHISU(4){
    		@Override
    		int Point(int runningTime) {
    			return runningTime * STAEMINA * 10;
    		}
    	},
    	
    	SEOTAEUNG(11){
    		@Override
    		int Point(int runningTime) {
    			return runningTime * STAEMINA * 15;
    		}
    	};
    	
    	private int value;
    	
    	protected final int STAEMINA; // 선수의 기본 능력치, protected로 선언해야 상수에서 접근 가능
    	
    	BasketballPlayers (int value) {
    		this.value = value;
    		STAEMINA = value;
    		System.out.println("생성자" + this.value);
    	
    	}
    	
    	abstract int Point(int runningTime);
    }

    위 코드처럼 Point라는 추상 메소드를 각가의 상수에서 overriding해서 자신만의 메소드를 구현할 수 있다.

     

     

    마지막으로 알아두어야 할 특징은 enum에 속한 상수의 비교에는 ==과 compareTo()는 사용가능 하다.

    하지만 =,>,>=,<,<=,<>과 같은 비교연산자는 사용할 수 없다.

    enum은 하나의 클래스이기 때문에 객체와 객체는 서로 비교연산자를 사요하여 연산을 할 수 없기 때문에 enum에서는 비교연산자를 지원하지 않는 것이다.

     

     

     

    2. enum이 제공하는 메소드 (values()와 valueOf())

    enum에서 제공하는 메소드인 values()와 valueOf()메소드를 알아보자.

    이 두 가지 메서드는 자바의 모든 enum(열거형)에 컴파일러가 자동으로 추가해준다.

     

    먼저 values()이다.

    해당 enum의 멤버인 상수들을 배열로 만들어 반환해준다.

    이렇게 배열로 반환해주면 for문을 사용해서 배열에 담긴 요소를 확인하면 상수의 이름이 담겨있는 것을 확인할 수 있다.

     

     

    다음으로 valueOf()이다.

    valueOf() 메소드는 매개변수로 전달된 값과 일치하는 해당 enum의 상수를 반환하는 기능을 한다.

     

     

     

    3. java.lang.Enum

    모든 enum은 내부적으로 java.lang.Enum 클래스를 부모 클래스로 가진다.

    java.lang.Enum 클래스가 가지고 있는 메소드는 아래의 표와 같다.

    메소드 설명
    Class getDeclaringClass() 열거형의 Class객체를 반환
    String name() 열거형 상수의 이름을 문자열로 반환
    int ordinal() 열거형 상수가 정의된 순서를 반환(0부터 시작)
    T valueOf(Class enumType, String name) 지정된 열거형에서 name과 일치하는 열거형 상수를 반환
    compareTo(E o) 지정된 객체보다 작은 경우 음의 정수, 동일한 경우 0, 크면 양의정수를 반환

     

    간단하게 name()메소드와 oridinal()메소드를 사용한 코드 실행결과를 보자.

    먼저, ordinal()이다.

    ordinal은 enum의 멤버인 상수의 순서번호를 반환한다.

    BasketballPlayers Enum에서 KANGBAEKHO, CHAECHISU, SEOTAEUNG 순서로 선언했기 때문에 각각 0번부터 시작하여 순서번호가 매겨진 것이다.

     

     

    다음으로 name() 메소드이다.

    name은 enum의 상수의 이름을 반환해준다.

    하지만, name()메소드 없이도 해당 상수의 이름을 가져올 수 있는데 차이점은 객체의 타입이다.

     

     

     

    4. EnumSet

    EnumSet은 JDK 1.5에서 등장한 java.util패키지의 클래스로 열거형을 위해 만들어진 특별한 Set 인터페이스 구현체이다.

    EnumSet은 Set 인터페이스를 기반으로한 열거형 타입으로 지정한 요소들을 쉽고 빠르게 배열처럼 요소들을 다룰수 있는 기능을 제공한다.

    또한, 비트연산을 이용해 메모리 공간을 적게 차지하고 속도가 빠르고 enum과 static 타입의 메소드들로 구성되어있어 안정성을 최대한 추구하면서도 편리한 사용이 가능하다.

    EnumSet의 상속 구조는 아래와 같다.

    출처: https://www.geeksforgeeks.org/enumset-class-java/

     

    EnumSet의 특징은 다음과 같다.

    - EnumSet은 AbstractSet 클래스를 상속하고 Set 인터페이스를 구현한다.

    - 오직 열거형 상수만을 값으로 가질 수 있다. 또한 모든 값은 같은 enum type이어야 한다.

    - null value를 추가하는 것을 허용하지 않는다. NullPointerException을 던지는 것도 허용하지 않는다.

    - ordinal 값의 순서대로 요소가 저장된다.

    - tread-safe하지 않다. 동기식으로 사용하려면 Collections.synchronizedMap을 사용하거나, 외부에서 동기화를 구현해야한다.

    - 모든 메서드는 arithmetic bitwise operation을 사용하기 때문에 모든 기본 연산의 시간 복잡도가 O(1)이다.

     

     

     

     

     

    <참고자료>

    https://wisdom-and-record.tistory.com/52

    https://velog.io/@kwj1270/Enum

    https://parkadd.tistory.com/50

    https://velog.io/@ljs0429777/11%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C-Enum

    댓글

Designed by Tistory.