Enum 클래스란?
Enum 클래스 정의
- Enum이란 Enumeration의 앞 글자로 열거라는 의미를 갖습니다. 관령이 있는 상수들의 집합니다.
자바에서는 final로 String과 같은 문자열이나 숫자들을 나타내는 기본 자료형의 값을 고정할 수 있습니다.
이렇게 고정된 값을 상수라고 합니다. 영어로는 constant입니다.
어떤 클래스가 상수만으로 작성되어 있으면 반드시 class로 선언할 필요는 없습니다.
이럴 때 class로 선언된 부분에 enum이라고 선언하면 이 객체는 상수의 집합입니다. 라는 것을 명시적으로 나타냅니다. - 기존에는 인터페이스나 클래스 내에서 상수를 선언함으로써 상수를 관리 하였는데 클래스 내에서 선언하는 부분은 네이밍이 겹칠 수 있고 불필요하게 상수가 많아지는 단점이 있습니다.
인터페이스로 관리하는 경우 이런 부분은 줄어들지만 여전히 IDE의 지원을 적극적으로 받을 수 없고 타입 안정성이 떨어지는 단점을 가지고 있습니다. 이를 보완하며 나온 것이 Enum입니다.
Enum 클래스 특징
- 클래스를 상수처럼 사용할 수 있습니다.
- 아래의 코드는 Enum Class의 예제입니다.
생성자가 존재하지만 Default 생성자는 private 로 되어 있으며 public 으로 변경하는 경우 컴파일 에러가 발생합니다.
즉, 다른 클래스나 인터페이스에서의 상수선언이 클래스 로드 시점에서 생성되는 것 처럼 Enum 또한 생성자가 존재하지만 클래스가 로드되는 시점에서 생성되기 때문에 임의로 생성하여 사용 할 수 없습니다. 이를 사용하고자 한다면 Rank.FIVE 와 같은 형태로 상수처럼 사용하면 됩니다.
public enum Rank { THREE(3, 4_000), FOUR(4, 10_000), FIVE(5, 30_000); private final int match; private final int money; private int count; Rank(int match, int money) { // Default 생성자는 private 으로 설정되어 있음. this.match = match; this.money = money; }
- 아래의 코드는 Enum Class의 예제입니다.
- Enum 클래스를 구현하는 경우 상수 값과 같이 유일하게 하나의 인스턴스가 생성되어 사용됩니다.
- Enum 클래스에서 선언한 상수들은 클래스가 로드될 때 하나의 인스턴스로 생성되어 싱글톤 형태로 어플리케이션 전체에서 사용됩니다.
싱글톤 으로 사용되기 때문에 각각의 Enum 인스턴스에 변수를 추가하여 사용하는 것은 Multi Thread 환경에서 위험할 수 있습니다.
아래의 예시를 보면 각각의 인스턴스에 count 라는 변수가 추가되어 있는데 외부에서 각 등수에 맞게 plusCount() 를 호출 할 수 있다. 하지만 멀티 쓰레드 환경에서는 각 인스턴스의 count가 공유되고 있기 때문에 조심해야 한다.public enum Rank { THREE(3, 4_000), FOUR(4, 10_000), FIVE(5, 30_000); private final int match; private final int money; private int count; Rank(int match, int money) { this.match = match; this.money = money; } public void plusCount() { this.count++; } }
- Enum 클래스에서 선언한 상수들은 클래스가 로드될 때 하나의 인스턴스로 생성되어 싱글톤 형태로 어플리케이션 전체에서 사용됩니다.
- 서로 관련 있는 상수 값들을 모아 Enum으로 구현하는 경우 유용합니다.
- 클래스와 같은 문법 체계를 따릅니다.
- 상속을 지원하지 않습니다.
- 모든 Enum들은 내부적으로 java.lang.enum 클래스에 의해 상속됩니다. 자바는 다중 상속을 지원하지 않기 때문에 Enum은 다른 클래스를 상속받을 수 없습니다. 참고로 toString() 메서드는 상수의 이름을 리턴하도록 구현되어 있습니다.
-
public static void main(String[] args) { System.out.println(Rank.FIVE.toString()); } // 결과 : FIVE
- 기존에 상속받고 있는 클래스가 존재하기 때문에 다중 상속은 지원하지 않지만 다양한 인터페이스 들은 구현할 수 있습니다.
Enum 클래스의 내부 API
- 위에서 언급한 java.lang.Enum 클래스를 기본적으로 상속받고 있기 때문에 아래의 세 가지 메소드를 지원합니다.
(부모 클래스의 메소드라 사용 가능합니다.)- values()
- values() 는 Enum 클래스가 가지고 있는 모든 상수 값을 배열의 형태로 리턴 합니다. 참고로 단순히 String 의 형태로 단순 반환하는 것이 아니라 인스턴스를 반환하는 것입니다.
즉, Enum 클래스가 가지고 있는 모든 인스턴스를 배열에 담아 반환하는 것입니다.-
public static void main(String[] args) { Rank[] values = Rank.values(); for(int i = 0; i< values.length; i++) { System.out.println(values[i]); } } // 실행 결과 : THREE, FOUR, FIVE
-
- values() 는 Enum 클래스가 가지고 있는 모든 상수 값을 배열의 형태로 리턴 합니다. 참고로 단순히 String 의 형태로 단순 반환하는 것이 아니라 인스턴스를 반환하는 것입니다.
- valueOf()
- valueOf() 메서드는 String 을 파라미터로 받는데 인자로 들어온 String 과 일치하는 상수 인스턴스가 존재하면 그 인스턴스를 반환합니다.
이 또한 마찬가지로 단순히 문자열을 반환하는 것이 아니라 인자로 들어온 문자열과 일치하는 인스턴스를 반환하는 것입니다
-
public static void main(String[] args) { System.out.println(Rank.valueOf("THREE")); } // 실행 결과 : THREE
-
- valueOf() 메서드는 String 을 파라미터로 받는데 인자로 들어온 String 과 일치하는 상수 인스턴스가 존재하면 그 인스턴스를 반환합니다.
- ordinal()
- Enum 클래스 내부에 있는 상수들의 Index 를 리턴하는 메소드입니다. 배열과 마찬가지로 0부터 인덱스가 시작하며 인덱스의 length 는 상수의 수 - 1 이다.
-
public static void main(String[] args) { Rank[] values = Rank.values(); for(int i = 0; i< values.length; i++) { System.out.println(values[i] + "인덱스는 : " + values[i].ordinal()); } } // 실행 결과 THREE인덱스는 : 0 FOUR인덱스는 : 1 FIVE인덱스는 : 2
-
- Enum 클래스 내부에 있는 상수들의 Index 를 리턴하는 메소드입니다. 배열과 마찬가지로 0부터 인덱스가 시작하며 인덱스의 length 는 상수의 수 - 1 이다.
- values()
Enum 클래스의 사용 및 활용
- 단순히 위의 예시 만으로는 Enum의 효용에 대해 느끼기 힘든 것 같습니다.
물론 클래스에 상수가 많아 진다면 Enum을 활용하는 것도 방법이겠지만 구체적으로 어떠한 경우에서 사용해야 좋은 지에 대해서는 애매하다고 생각하였습니다. 그래서 인터넷에 있는 다양한 사례들을 아래에 정리해 보았습니다.
사례 들을 보며 어떤 식으로 사용하면 좋을지 본인이 판단하여 사용하시면 될 것 같습니다.- 데이터의 그룹화 및 관리에 용이
- 관련되어 있지만 관련성을 표시하기 힘든 형태의 데이터를 한 곳에서 관리할 수 있습니다.
예를 들어 경기 이후 승리한 사람과 패배한 사람을 리스트로 관리한다고 생각해보면, 이 경우 리스트의 변수명을 통해 관리하거나 Player 라는 클래스를 각기 상속 받는 형태로 관리하는 등의 방법으로 관리 될 수 있습니다. 하지만 Enum을 사용한다면 보다 명확한 방법으로 이들의 관계를 가시적으로 표현할 수 있습니다.-
public enum Winner { WINNER("승리", Arrays.asList("kyle","pobi","hello","world")), LOSER("패배", Arrays.asList("hodol","dunddoung","rutgo"); private final String winner; private final List<String> list; Winner(String winner, List<String> list) { this.winner = winner; this.list = list; } }
-
- 단순히 관련 있는 데이터를 모아서 관리할 뿐만 아니라 자바에서 Enum은 완전한 클래스의 형태를 보이고 있기 때문에 관련 로직을 같은 Enum 클래스 내에서 관리할 수 있기 때문에 상태와 행위를 한 곳에서 관리 할 수 있습니다. 아래의 예들을 통해 승리자가 누구인지, 승리자가 몇 명인지 등 승리와 관련된 로직을 한 곳에서 관리할 수 있는 장점이 있습니다.
-
public boolean isWinner(String name) { return WINNER.list.contains(name); } public int getWinnerSize() { return WINNER.list.size(); }
-
- 비슷한 예로 구매한 로또와 당첨번호가 같은 갯수, 그리고 각 갯수에 대응하는 상금 등과 같이 관련된 데이터도 하나의 Enum으로 관리할 수 있습니다.
로또의 당첨 개수와 당첨 금액을 관리하는 하나의 클래스로 클래스를 분리하지 않고도 두 데이터(일치한 갯수와 당첨 금액)의 연관성을 명확하게 보여줍니다.
뿐만 아니라 일치한 수를 입력하면 그 수와 일치하는 인스턴스를 반환하는 메소드 등 상태와 연관있는 행위 또한 한 곳에서 관리할 수 있는 장점이 있습니다.-
public enum Statistic { THREE(3, 5000), FOUR(4, 50_000), FIVE(5, 1_500_000), BONUS(5, 3_000_000), SIX(6, 2_000_000_000); private final int matchingNumbers; private final int prize; Statistic(int matchingNumbers, int prize) { this.matchingNumbers = matchingNumbers; this.prize = prize; } public static Statistic getRank(int numberOfMatch) { return Arrays.stream(values()) .filter(statistic -> statistic.matchingNumbers == numberOfMatch) .findFirst() .orElseThrow(new IllegalArgumentException("일치하는 번호가 3미만입니다.")) }
-
- 관련되어 있지만 관련성을 표시하기 힘든 형태의 데이터를 한 곳에서 관리할 수 있습니다.
- Lambda를 활용한 Enum 사용 극대화
- Enum은 자바의 익명함수 인터페이스 및 커스터마이징 익명 함수를 이용하면서 다양한 방법으로 더욱 활용 될 수 있습니다. 간단한 계산기 프로그램을 살펴보겠습니다.
-
public interface Calculator { int calculate(int a, int b); } class Plus implements Calculator{ @Override public int calculate(int a, int b) { return a + b; } } class Minus implements Calculator{ @Override public int calculate(int a, int b) { return a - b; } } class Multiply implements Calculator{ @Override public int calculate(int a, int b) { return a * b; } } class Divide implements Calculator{ @Override public int calculate(int a, int b) { if(b == 0) throw new ArithmeticException(); return a / b; } }
-
- 계산을 위해 Calculator라는 상위 인터페이스를 두고 더하기, 빼기, 곱하기, 나누기 부분이 각각 이를 구현하는 방식을 취하고 있습니다.
아래는 위의 코드를 Enum 으로 옮긴 코드입니다. 물론 함수형 인터페이스 자체의 강점으로 인해 코드가 간결해진 부분도 있지만 더하기와 빼기 등 연산에 대한 명확한 상수명을 함께 가지면서 내부 계산 로직 또한 같이 가지고 있다는 점이 장점입니다.-
import java.util.function.BiFunction; public enum Operator { PLUS("더하기", (a, b) -> (a + b)), MINUS("빼기", (a, b) -> (a - b)), MULTIPLY("곱하기", (a, b) -> (a * b)), DIVIDE("나누기", (a, b) -> (a / b)); private final String name; private final BiFunction<Double, Double, Double> biFunction; Operator(String name, BiFunction<Double, Double, Double> biFunction) { this.name = name; this.biFunction = biFunction; } public Double calculate(double a, double b) { return this.biFunction.apply(a,b); } }
-
- Enum을 통해 기존에 고정되어 있는 특정 상수 혹은 Enum의 value들에 대해서 관리하는 메소드 등으로 구성되어 있었다면 함수형 인터페이스를 통해 Enum의 각 상수 인스턴스도 조금 더 능동적인 프로그래밍이 가능해졌고, 상수와 함수식 및 정보를 같이 관리하기 때문에 상태와 행위 둘을 모두 같은 곳에서 관리한다는 점에서 장점을 가집니다.
- Enum은 자바의 익명함수 인터페이스 및 커스터마이징 익명 함수를 이용하면서 다양한 방법으로 더욱 활용 될 수 있습니다. 간단한 계산기 프로그램을 살펴보겠습니다.
- 데이터의 그룹화 및 관리에 용이
Enum 클래스의 추가적인 장점
- IDE의 적극적인 지원
- 자동완성, 오타검증, 텍스트 리팩토링 등등
- 허용하고자 하는 값으로 제한하기
- 리팩토링시 변경 범위가 최소화 됩니다.
- 상수를 Enum에서 관리하기 때문에 내용의 추가가 필요하더라도, Enum 코드외에 수정할 필요가 없습니다.
- 다른 언어와 달리 Java 에서는 Enum 이 완전한 기능을 가지고 있는 클래스라서 상수와 관련된 작업들을 추가할 수 있습니다.
'JAVA' 카테고리의 다른 글
[JAVA] - 객체 생성과 참조형 변수 (0) | 2024.01.12 |
---|---|
[JAVA] - 클래스 설계 (0) | 2024.01.12 |
[JAVA] - 싱글톤(Singleton) 패턴이란? (0) | 2024.01.11 |
[JAVA] - 객체지향 프로그래밍 이해하기 (0) | 2024.01.11 |
[JAVA] - 컬렉션 (1) | 2024.01.09 |