enum 정의하는 방법
enum이 제공하는 메소드 (values()와 valueOf())
java.lang.Enum
EnumSet
열거형 상수라고 부른다.
열거형은 서로 연관된 상수들의 집합이다.
자바에서는 final로 String과 같은 문자열이나 숫자들을 나타내는 기본 자료형의 값을 고정할 수 있다.
이렇게 고정된 값을 상수라고 한다. (영어로는 constant)
어떤 클래스가 상수만으로 작성되어 있으면 반드시 class 선언할 필요는 없다.
이럴 때 class로 선언된 부분에 enum이라고 선언하면 이 객체는 상수의 집합이다, 라는 것을 명시적으로 나타낸다.
자바1.5 버전 이후에는 상수를 정의할 때 enum을 많이 사용한다.
public class EnumTest {
private final static int MONDAY = 1;
private final static int TUESDAY = 2;
public static void main(String[] args) {
int day = MONDAY;
switch (day){
case MONDAY:
System.out.println("월요일");
break;
case TUESDAY:
System.out.println("화요일");
break;
}
}
}
위 코드는 final
을 사용하여 한번 지정하면 바뀌지 않게 설정하고 staic
을 사용해 메모리에 한번 만 할당 되게 설정하였다.
또한 이름이 있어 이것이 무엇을 의미하는지 한 눈에 알 수 있게 했다.
하지만 여기에도 안 좋은 문제가 하나 있는데, 예를 들어 위 예제에 month
에 대한 상수를 추가해야 하면 상수가 너무 많아지고 한눈에 상수들이 어떤 것에 관련된 것인지 보기 힘들어 진다.
또한, 각각의 상수의 집합에서 같은 이름으로 정의된 상수가 있따면 중복된 이름이기 때문에 컴파일 오류또한 발생한다.
그래서 다음 방법으로 class 또는 인터페이스를 사용하여 각각의 집합끼리 상수가 정의되고 중복된 이름이 있어도 오류가 발생하지 않게 할 수 있다.
public class EnumTest {
interface Day{
int MONDAY = 1;
int TUESDAY = 2;
}
interface MONTH{
int JANUARY = 1;
int FEBRUARY = 2;
}
public static void main(String[] args) {
if (Day.MONDAY == MONTH.JANUARY){
System.out.println("같음");
}
int day = Day.MONDAY;
switch (day){
case Day.MONDAY:
System.out.println("월요일");
break;
case Day.TUESDAY:
System.out.println("화요일");
break;
}
}
}
위 코드에서는 깔끔하게 두개의 특징을 갖는 상수의 집합을 작성했다.
각각의 집합에서는 이름이 같은 상수도 정의할 수 있다.
또한 interface 에서 선언된 변수는 public static final 속성을 생략할 수 있는 특징을 이용하여 코드를 조금 더 간결하게 작성할 수 있다.
하지만 이 방법에도 문제가 있는데, 서로 다른 집합에 정의된 상수들을 서로 비교하면 안된다.
다른 집합의 상수를 비교하면 컴파일 단계에서 에러를 확인할 수 있어야 하지만, 저 코드는 확인할 수 없기 때문에 런타임 단계에서 문제가 발생할 수 있다.
public class EnumTest {
public static void main(String[] args) {
if (Day.MONDAY == Month.JANUARY){
System.out.println("같음");
}
Day day = Day.MONDAY;
switch (day){
case Day.MONDAY:
System.out.println("월요일");
break;
}
}
}
class Day{
public final static Day MONDAY = new Day();
public final static Day TUESDAY = new Day();
}
class Month{
public final static Month JANUARY = new Month();
public final static Month FEBRUARY = new Month();
}
여기서는 interface로 선언된 상수를 class로 변경했다.
각각의 상수들의 타입을 자신의 상수 집합의 이름으로 지정하였고, 자기 자신을 인스턴스화 한 값을 할당한다.
이 말은 각각의 상수들이 서로 다른 데이터를 의미한다는 말이다, 하지만 같은 집합의 상수들은 같은 데이터 타입을 갖는다.
즉 데이터 타입은 같지만 서로 다른 데이터 값을 가지고 있다.
if (Day.MONDAY == Month.JANUARY)
이 코드는 컴파일 에러가 발생한다, 이유는 서로 다른 데이터 타입은 비교할 수 없다는 의미이다.
아까 런타임에서 발생할 수 있는 에러를 컴파일 단계에서 검출하도록 수정했다,
하지만 여기서 우리가 작성한 상수들을 못 쓰는 경우가 발생한다.
Switch 문에서는 사용하지 못 하는데 switch (day)
에서는 에러가 발생한다.
왜냐하면 switch 문의 조건에 들어가는 데이터 타입이 제한적이기 때문이다.
그래서 이러한 문제가 위 코드를 이용한 상수의 단점이라고 볼 수 있다.
enum Day{
MONDAY,
TUESDAY
}
enum Month{
JANUARY,
FEBRUARY
}
public class EnumTest {
public static void main(String[] args) {
Day day = Day.MONDAY;
switch (day){
case MONDAY:
System.out.println("월요일");
case TUESDAY:
System.out.println("화요일");
}
}
}
enum 이라는 키워드를 사용하고 상수의 집합을 의미하는 이름을 입력하면 된다.
그리고 각각의 상수들의 이름을 차례대로 나열하면 상수가 정의된다.
또한 switch 문의 레이블을 조건으로 넘어온 데이터 타입을 알고 있다.
그래서 각각의 레이블에서는 enum 의 데이터 타입을 생략하고 상수만 입력할 수 있다.
class Day{
public final static Day MONDAY = new Day();
public final static Day TUESDAY = new Day();
}
enum Day{
MONDAY,
TUESDAY
}
위의 코드 2개는 똑같은 기능을 한다.
하지만 enum을 사용한 것이 훨씬 더 간결하고 가독성이 좋다.
enum 은 각 열거형 상수에 추가 속성을 부여할 수 있다.
enum Day{
MONDAY("월요일"),
TUESDAY("화요일");
private String name;
Day(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
생성자의 파라미터를 통해 추가 속성을 enum 클래스의 필드에 설정해주고, getter 메소드를 할당해 해당 속성을 필요할 때에 가져다 쓸 수 있게 한다.
이렇게 enum은 상수에 어떤 데이터를 연관시킬 수 있고 계속 진화해 완벽한 추상체가 될 수 있다.
// enum showing Mobile prices
enum Mobile {
Samsung(400),
Nokia(250),
Motorola(325);
int price;
Mobile(int p) {
price = p;
}
int showPrice() {
return price;
}
}
public class EnumDemo {
public static void main(String args[]) {
System.out.println("CellPhone List:");
for(Mobile m : Mobile.values()) {
System.out.println(m + " costs " + m.showPrice() + " dollars");
}
Mobile ret = Mobile.valueOf("Samsung");
System.out.println("Selected : " + ret);
Mobile re1t = Mobile.valueOf("Apple");
System.out.println("Selected : " + ret);
}
Selected : Samsung
}
Enum에 열거되어 있는 상수들을 배열 형태로 반환해주는 메소드
위에 코드에서 values를 이용해 배열 형식으로 값을 가져 왔다.
CellPhone List:
Samsung costs 400 dollars
Nokia costs 250 dollars
Motorola costs 325 dollars
// 메소드 선언
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)
// 파리미터
enumType - 상수를 반환활 열거형 형식의 class 개체
name - 반환할 상수의 이름
메소드는 지정된 이름을 가진 지정된 열거형의 상수를 반환한다, 즉 메소드이 파리미터 값과 동일한 상수를 반환.
이름은 이 유형에서 열거형 상수를 선언하는 데 사용된 식별자와 정확하게 일치해야 한다.
만약 열거되어 있는 상수가 없는 경우 IllegalArgumentException 발생시킨다.
이외에도 Ordinal() 메소드가 있는데 Enum에 열거되어 있는 상수의 index를 반환하는 메소드이다.
System.out.println("CellPhone List:");
for(Mobile m : Mobile.values()) {
System.out.println(m + " costs " + m.showPrice() + " dollars" + m.ordinal());
}
//
CellPhone List:
Samsung costs 400 dollars0
Nokia costs 250 dollars1
Motorola costs 325 dollars2
자바의 모든 Enum 타입의 상위 클래스이다.
public static void main(String args[]) {
Mobile mobile = Mobile.Samsung;
System.out.println(mobile.name());
}
// Samsung
public static void main(String args[]) {
Mobile mobile = Mobile.Samsung;
System.out.println(mobile.toString());
}
// Samsung
public static void main(String args[]) {
Mobile mobile = Mobile.Samsung;
Mobile mobile1 = Mobile.Motorola;
System.out.println(mobile.equals(mobile1));
}
//false
public static void main(String args[]) {
Mobile mobile = Mobile.Samsung;
Mobile mobile1 = Mobile.Motorola;
Mobile mobile2 = Mobile.Samsung;
System.out.println(mobile.compareTo(mobile2));
}
// 0
public static void main(String args[]) {
Mobile mobile = Mobile.Samsung;
Mobile mobile1 = Mobile.Motorola;
Mobile mobile2 = Mobile.Samsung;
System.out.println(mobile.compareTo(mobile1));
}
// -2
열거형 타입 상수들을 하나의 배열처럼 사용할 수 있게 해준다.
EnumSet은 비트 연산을 이용하기 때문에 메모리의 공간을 작게 차지할 뿐만 아니라 속도면에서 빠른모습을 보여준다.
EnumSet은 new 연산자를 이용해 선언할 수 없기 때문에 EnumSet.allOf(열거형 클래스.class);
형식으로 선언할 수 있다.
// EnumSet 선언
EnumSet<Month> enumSet = EnumSet.allOf(Month.class);
System.out.println(enumSet);
// EnumSet을 비우는 메소
enumSet = EnumSet.noneOf(Month.class);
System.out.println(enumSet);
// EnumSet.of() : 파라미터로 전달되는 열거형 상수를 제외한 상수를 담아 배열로 리턴하는 메소드
enumSet = EnumSet.of(Month.JANUARY);
System.out.println(enumSet);
// 파라미터로 들어오는 EnumSet을 제외한 상수를 담아 배열로 리
enumSet = EnumSet.complementOf(enumSet);
System.out.println(enumSet);
// 파라터로 전해지는 범위의 열거형 상수를 담아 배열로 리턴
enumSet = EnumSet.range(Month.JANUARY,Month.FEBRUARY);
System.out.println(enumSet);
}
//
[JANUARY, FEBRUARY]
[]
[JANUARY]
[FEBRUARY]
[JANUARY, FEBRUARY]
enum 확장하는법
package com.example.algorithm.enn;
public enum BasicStringOperation {
TRIM("Removing leading and trailing spaces."),
TO_UPPER("Changing all characters into upper case."),
REVERSE("Reversing the given string.");
private String description;
// constructor and getter
}
public enum ExtendedStringOperation extends BasicStringOperation {
MD5_ENCODE("Encoding the given string using the MD5 algorithm."),
BASE64_ENCODE("Encoding the given string using the BASE64 algorithm.");
private String description;
// constructor and getter
}
enum 이 enum 을 extends 하면 컴파일 오류가 발생한다
Cannot inherit from enum BasicStringOperation
열거형은 상속할 수 없습니다 (다중 상속 상황이 된다<최종 클래스>)
하지만 인터페이스를 통해서 확장을 할 수 있다.
public enum BasicStringOperation implements StringOperation{
TRIM("Removing leading and trailing spaces."),
TO_UPPER("Changing all characters into upper case."),
REVERSE("Reversing the given string.");
private String description;
BasicStringOperation(String s) {
}
public String getDescription() {
return description;
}
}
public enum ExtendedStringOperation implements StringOperation {
MD5_ENCODE("Encoding the given string using the MD5 algorithm."),
BASE64_ENCODE("Encoding the given string using the BASE64 algorithm.");
ExtendedStringOperation(String description) {
this.description = description;
}
private String description;
@Override
public String getDescription() {
return description;
}
}
package com.example.algorithm.enn;
public class Application {
public String getOperationDescription(BasicStringOperation basicStringOperation){
return basicStringOperation.getDescription();
}
public String getOperationDescription(StringOperation stringOperation) {
return stringOperation.getDescription();
}
}
다른 enum 에 지정된 값들을 오버로딩을 통해 가져와서 사용하면 확장된 enum을 구현해 사용할 수 있다.