본문 바로가기

WhiteShip Java Study : 자바 처음부터 멀리까지

enum

선장님과 함께하는 자바 스터디입니다.

자바 스터디 Github

github.com/whiteship/live-study

 

whiteship/live-study

온라인 스터디. Contribute to whiteship/live-study development by creating an account on GitHub.

github.com

나의 Github

github.com/cmg1411/whiteShip_live_study

 

cmg1411/whiteShip_live_study

✍ 자바 스터디할래. Contribute to cmg1411/whiteShip_live_study development by creating an account on GitHub.

github.com

 

  • enum 정의하는 방법
  • enum이 제공하는 메소드 (values()와 valueOf())
  • java.lang.Enum
  • EnumSet

 

 

enum

  • 열거형이라고도 부른다.
  • 한정된 타입만 갖고 상수만 모아놓은 데이터 타입.
  • Type-Safe 한 문제를 해결하기 위해 나왔다.

enum 의 정의 방법

기본적으로 enum은 클래스와 같다. 클래스를 선언할 때 붙이는 class 대신 enum 을 붙이면 된다.

접근 지정자 역시 클래스와 같이 public, package-private 만 허용된다.

 

public enum Week {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

 

enum 은 특수한 형태의 클래스이므로 인스턴스 변수, 생성자, 메서드를 쓸 수 있다.

 

public enum Week {
    MONDAY("월요일"),
    TUESDAY("화요일"),
    WEDNESDAY("수요일"),
    THURSDAY("목요일"),
    FRIDAY("금요일"),
    SATURDAY("토요일"),
    SUNDAY("일요일");

    private String korean;

    EnumEx(String korean) {
        this.korean = korean;
    }
    
    public String getWeekKorean() {
        return korean;
    }
}

인스턴스 변수를 선언하고 생성자로 인스턴스 변수값을 초기화해준다.

그리고 그 값을 enum 상수 이후에 () 안에 나열한다.

 

MONDAY("월요일") 로 생성자를 호출한다는 것을 잘 보면 이런 의문이 생긴다.

그럼 MONDAY는 객체인가 ?

정답은 맞다. 이에 대해서는 조금 있다가 자세히 알아보자.

 

enum의 사용은 아래와 같다.

    public static void main(String[] args) {
        Week today = Week.FRIDAY;
    }

 

enum 이전의 Type-safe 문제

  • 상수를 정의하는 방법은 final static 이라고 잘 알려져 있다.
  • enum 이 없을 때에는 상수를 사용하는 클래스나 인터페이스에서 상수를 정의하여 사용하였다.
  • 하지만 이 상수들은 type-safe 하지 못하다.
  • 아래 예를 보자.
타입에 불안정적이다 : 컴파일 타임에 타입을 구분 하지 못해 Runtime 시 타입으로 인한 문제가 발생하는 것.
Type Safe 하다 : 타입을 판별 할 수 있어 타입이 다르면 Runtime시가 아닌 컴파일시 문제를 잡을 수 있는 것.

문제는 항상 컴파일 타임에 발견하는 것이 좋다.
public class notTypeSafe {
    public static void main(String[] args) {
        if (FruitConst.MELON == MusicApp.MELON) {
            System.out.println("이게 왜 같아 !");
        }
    }
}

class FruitConst {
    public static final int APPLE = 1;
    public static final int MELON = 2;
    public static final int PITCH = 3;
}

class MusicApp {
    public static final int BUGS = 1;
    public static final int MELON = 2;
    public static final int APPLEMUSIC = 3;
}

 

FruitConst.MELON 과 MusicApp.MELON 은 둘 다 값이 1이다.

하지만 음악 앱의 멜론과 과일 멜론은 분명히 다르다.

또, 다른 클래스에서 선언했다. 이 둘은 분명히 다른 개체이다.

 

하지만 == 를 이용한 동일성 검사에서는 같다고 나온다.

이것이 type-safe 하지 못한 상수이다. 이 둘이 같다고 처리한다면 분명히 런타임시에 오류가 발생할 코드가 있을 것이다.

 

오타로 인해 오류가 발생할 수 있는 상황도 타입 세이프티하지 않다고 한다.

일례로, JPA 의 queryDSL 도 타입 세이프티를 위한 방법이라 할 수 있다.

 

Type-safe 한 enum

public class TypeSafeEnum {
    public static void main(String[] args) {
        FruitConst2 fruit_melon = FruitConst2.MELON;
        MusicApp2 music_melon = MusicApp2.MELON;
        
        if (fruit_melon == music_melon) {
            System.out.println("이게 왜 같아!");
        }

    }
}

enum FruitConst2 {
    APPLE,
    MELON,
    PITCH;
}

enum MusicApp2 {
    BUGS,
    MELON,
    APPLEMUSIC;
}

이 코드는 애초에 if 문의 조건에서 컴파일 에러가 뜬다.

다른 타입끼리는 비교할 수 없다는 것이다.

 

이것이 Type-safe 하다는 의미이다.

 

 

java.lang.Enum

  • java.lang 패키지에는 Enum 이라는 클래스가 있다.
  • 우리가 배운 enum 은 클래스의 일종인데 이 클래스는 뭐지? 라는 생각이 들 수 있다.
  • 먼저 enum을 정의하고 바이트코드를 뽑아보자. (위의 일주일 enum 바이트코드이다)
// class version 52.0 (52)
// access flags 0x4031
// signature Ljava/lang/Enum<Lcom/whiteship/white_ship_study/week11/Week;>;
// declaration: com/whiteship/white_ship_study/week11/Week extends java.lang.Enum<com.whiteship.white_ship_study.week11.Week>
public final enum com/whiteship/white_ship_study/week11/Week extends java/lang/Enum {

  // compiled from: Week.java

  // access flags 0x4019
  public final static enum Lcom/whiteship/white_ship_study/week11/Week; MONDAY

  // access flags 0x4019
  public final static enum Lcom/whiteship/white_ship_study/week11/Week; TUESDAY

  // access flags 0x4019
  public final static enum Lcom/whiteship/white_ship_study/week11/Week; WEDNESDAY

  // access flags 0x4019
  public final static enum Lcom/whiteship/white_ship_study/week11/Week; THURSDAY

  // access flags 0x4019
  public final static enum Lcom/whiteship/white_ship_study/week11/Week; FRIDAY

  // access flags 0x4019
  public final static enum Lcom/whiteship/white_ship_study/week11/Week; SATURDAY

  // access flags 0x4019
  public final static enum Lcom/whiteship/white_ship_study/week11/Week; SUNDAY

  // access flags 0x2
  private Ljava/lang/String; korean

  // access flags 0x101A
  private final static synthetic [Lcom/whiteship/white_ship_study/week11/Week; $VALUES

  // access flags 0x9
  public static values()[Lcom/whiteship/white_ship_study/week11/Week;
   L0
    LINENUMBER 3 L0
    GETSTATIC com/whiteship/white_ship_study/week11/Week.$VALUES : [Lcom/whiteship/white_ship_study/week11/Week;
    INVOKEVIRTUAL [Lcom/whiteship/white_ship_study/week11/Week;.clone ()Ljava/lang/Object;
    CHECKCAST [Lcom/whiteship/white_ship_study/week11/Week;
    ARETURN
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x9
  public static valueOf(Ljava/lang/String;)Lcom/whiteship/white_ship_study/week11/Week;
    // parameter mandated  name
   L0
    LINENUMBER 3 L0
    LDC Lcom/whiteship/white_ship_study/week11/Week;.class
    ALOAD 0
    INVOKESTATIC java/lang/Enum.valueOf (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
    CHECKCAST com/whiteship/white_ship_study/week11/Week
    ARETURN
   L1
    LOCALVARIABLE name Ljava/lang/String; L0 L1 0
    MAXSTACK = 2
    MAXLOCALS = 1

  // access flags 0x2
  // signature (Ljava/lang/String;)V
  // declaration: void <init>(java.lang.String)
  private <init>(Ljava/lang/String;ILjava/lang/String;)V
    // parameter synthetic  $enum$name
    // parameter synthetic  $enum$ordinal
    // parameter  korean
   L0
    LINENUMBER 14 L0
    ALOAD 0
    ALOAD 1
    ILOAD 2
    INVOKESPECIAL java/lang/Enum.<init> (Ljava/lang/String;I)V
   L1
    LINENUMBER 15 L1
    ALOAD 0
    ALOAD 3
    PUTFIELD com/whiteship/white_ship_study/week11/Week.korean : Ljava/lang/String;
   L2
    LINENUMBER 16 L2
    RETURN
   L3
    LOCALVARIABLE this Lcom/whiteship/white_ship_study/week11/Week; L0 L3 0
    LOCALVARIABLE korean Ljava/lang/String; L0 L3 3
    MAXSTACK = 3
    MAXLOCALS = 4

  // access flags 0x1
  public getWeekKorean()Ljava/lang/String;
   L0
    LINENUMBER 19 L0
    ALOAD 0
    GETFIELD com/whiteship/white_ship_study/week11/Week.korean : Ljava/lang/String;
    ARETURN
   L1
    LOCALVARIABLE this Lcom/whiteship/white_ship_study/week11/Week; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x8
  static <clinit>()V
   L0
    LINENUMBER 4 L0
    NEW com/whiteship/white_ship_study/week11/Week
    DUP
    LDC "MONDAY"
    ICONST_0
    LDC "\uc6d4\uc694\uc77c"
    INVOKESPECIAL com/whiteship/white_ship_study/week11/Week.<init> (Ljava/lang/String;ILjava/lang/String;)V
    PUTSTATIC com/whiteship/white_ship_study/week11/Week.MONDAY : Lcom/whiteship/white_ship_study/week11/Week;
   L1
    LINENUMBER 5 L1
    NEW com/whiteship/white_ship_study/week11/Week
    DUP
    LDC "TUESDAY"
    ICONST_1
    LDC "\ud654\uc694\uc77c"
    INVOKESPECIAL com/whiteship/white_ship_study/week11/Week.<init> (Ljava/lang/String;ILjava/lang/String;)V
    PUTSTATIC com/whiteship/white_ship_study/week11/Week.TUESDAY : Lcom/whiteship/white_ship_study/week11/Week;
   L2
    LINENUMBER 6 L2
    NEW com/whiteship/white_ship_study/week11/Week
    DUP
    LDC "WEDNESDAY"
    ICONST_2
    LDC "\uc218\uc694\uc77c"
    INVOKESPECIAL com/whiteship/white_ship_study/week11/Week.<init> (Ljava/lang/String;ILjava/lang/String;)V
    PUTSTATIC com/whiteship/white_ship_study/week11/Week.WEDNESDAY : Lcom/whiteship/white_ship_study/week11/Week;
   L3
    LINENUMBER 7 L3
    NEW com/whiteship/white_ship_study/week11/Week
    DUP
    LDC "THURSDAY"
    ICONST_3
    LDC "\ubaa9\uc694\uc77c"
    INVOKESPECIAL com/whiteship/white_ship_study/week11/Week.<init> (Ljava/lang/String;ILjava/lang/String;)V
    PUTSTATIC com/whiteship/white_ship_study/week11/Week.THURSDAY : Lcom/whiteship/white_ship_study/week11/Week;
   L4
    LINENUMBER 8 L4
    NEW com/whiteship/white_ship_study/week11/Week
    DUP
    LDC "FRIDAY"
    ICONST_4
    LDC "\uae08\uc694\uc77c"
    INVOKESPECIAL com/whiteship/white_ship_study/week11/Week.<init> (Ljava/lang/String;ILjava/lang/String;)V
    PUTSTATIC com/whiteship/white_ship_study/week11/Week.FRIDAY : Lcom/whiteship/white_ship_study/week11/Week;
   L5
    LINENUMBER 9 L5
    NEW com/whiteship/white_ship_study/week11/Week
    DUP
    LDC "SATURDAY"
    ICONST_5
    LDC "\ud1a0\uc694\uc77c"
    INVOKESPECIAL com/whiteship/white_ship_study/week11/Week.<init> (Ljava/lang/String;ILjava/lang/String;)V
    PUTSTATIC com/whiteship/white_ship_study/week11/Week.SATURDAY : Lcom/whiteship/white_ship_study/week11/Week;
   L6
    LINENUMBER 10 L6
    NEW com/whiteship/white_ship_study/week11/Week
    DUP
    LDC "SUNDAY"
    BIPUSH 6
    LDC "\uc77c\uc694\uc77c"
    INVOKESPECIAL com/whiteship/white_ship_study/week11/Week.<init> (Ljava/lang/String;ILjava/lang/String;)V
    PUTSTATIC com/whiteship/white_ship_study/week11/Week.SUNDAY : Lcom/whiteship/white_ship_study/week11/Week;
   L7
    LINENUMBER 3 L7
    BIPUSH 7
    ANEWARRAY com/whiteship/white_ship_study/week11/Week
    DUP
    ICONST_0
    GETSTATIC com/whiteship/white_ship_study/week11/Week.MONDAY : Lcom/whiteship/white_ship_study/week11/Week;
    AASTORE
    DUP
    ICONST_1
    GETSTATIC com/whiteship/white_ship_study/week11/Week.TUESDAY : Lcom/whiteship/white_ship_study/week11/Week;
    AASTORE
    DUP
    ICONST_2
    GETSTATIC com/whiteship/white_ship_study/week11/Week.WEDNESDAY : Lcom/whiteship/white_ship_study/week11/Week;
    AASTORE
    DUP
    ICONST_3
    GETSTATIC com/whiteship/white_ship_study/week11/Week.THURSDAY : Lcom/whiteship/white_ship_study/week11/Week;
    AASTORE
    DUP
    ICONST_4
    GETSTATIC com/whiteship/white_ship_study/week11/Week.FRIDAY : Lcom/whiteship/white_ship_study/week11/Week;
    AASTORE
    DUP
    ICONST_5
    GETSTATIC com/whiteship/white_ship_study/week11/Week.SATURDAY : Lcom/whiteship/white_ship_study/week11/Week;
    AASTORE
    DUP
    BIPUSH 6
    GETSTATIC com/whiteship/white_ship_study/week11/Week.SUNDAY : Lcom/whiteship/white_ship_study/week11/Week;
    AASTORE
    PUTSTATIC com/whiteship/white_ship_study/week11/Week.$VALUES : [Lcom/whiteship/white_ship_study/week11/Week;
    RETURN
    MAXSTACK = 5
    MAXLOCALS = 0
}

 

1. java.lang.Enum 을 상속받는다.

public final enum com/whiteship/white_ship_study/week11/EnumEx extends java/lang/Enum {

제일 첫 부분이다.

그렇다. enum 클래스는 컴파일타임에 자동으로 java.lang.Enum 클래스를 상속받고 있다.

따라서 enum 클래스는 상속이 되지 않고, Enum 클래스에 정의된 함수들을 사용 가능하다.

 

2. enum 상수들은 객체이다.

  public final static enum Lcom/whiteship/white_ship_study/week11/Week; MONDAY

이 부분은 우리가 정의한 enum 안의 상수다.

public final static 을 사용하지 않았지만, 자동으로 모두 붙어 있다.

상수를 정의하던 것과 같다.

또한, EnumEx 형으로 만들어진 객체임을 알 수 있다.

 

따라서 우리는 이 상수 객체에 생성자를 만들 수 있고, 값을 가질 수 있고, 메서드를 가질 수 있게 할 수 있는 것이다.

 

3. enum 생성자는 private 이다.

  private <init>(Ljava/lang/String;ILjava/lang/String;)V

우리는 private으로 지정한 적 이 없는데 private 이다.

enum 은 고정된 상수 집합이라 하였다.

따라서 런타임에 우리는 이 enum 으로 객체를 만들면 안된다. 쉽게말해, 런타임시에 값이 바뀌면 안된다.

 

객체를 만들지 않게 하는 방법이 private 생성자만 제공하는 방법이다.

외부에서 접근할 수 있는 생성자가 없다면, 객체를 생성할 수 없다.

 

따라서 enum 의 모든 생성자에는 private 이 자동으로 붙는다.

 

또한 생성자가 없으면 enum 클래스를 상속할 수 도 없다.

 

이러한 특성 때문에 enum 은 싱글톤을 구현하는 이상적인 방법이 될 수 있기도 하다. (effective java)

 

 

 

바이트코드에서 알 수있는 정보는 몇가지 더 있는데, 이후 관련 블럭에서 알아보자.

 

 

 

 

 

Enum 이 제공하는 메서드

  • enum 타입은 자동으로 java.lang.Enum 을 상속받는다고 했다.
  • 그렇다면 우리는 Enum 에 정의된 함수들을 알아야 enum 상수들을 재대로 쓸 수 있다.

 

name 필드

  • Enum 클래스에는 name 이라는 필드가 있다.
  • 또 toString() 에는 return name을 하고 있다.
  • Enum 클래스에는 생성자가 하나 있는데 모양은 이렇다. (ordinal 은 순서이다.)
    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

 

그리고 위의 바이트코드의 생성자 부분을 다시 보자.

  private <init>(Ljava/lang/String;ILjava/lang/String;)V
    // parameter synthetic  $enum$name
    // parameter synthetic  $enum$ordinal
    // parameter  korean

enum 의 name, ordinal 이 컴파일하면서 자동으로 파라미터로 들어간다.

(synthetic 이 붙은 것은 컴파일타임에 자동으로 들어간다는 뜻 같다.. 확실하게는 모름)

 

 

그렇게 되면 우리가 정의한 생성자를 보면,

    EnumEx(String korean) {
        this.korean = korean;
    }

이 모양인데, name과 ordinal 은 아래와 같이 들어가는 것이다.

    EnumEx(String korean) {
    	super(name, ordinal);
        this.korean = korean;
    }

 

따라서, 우리가 정의한 enum 상수들은 우리가 정의한 인스턴스 필드와 name, ordinal 을 속성으로 가지게 되는 것이다.

name 은 enum 에 정의한 상수명이고, ordinal 은 상수를 여러개 선언했을때 각각의 순서이다.

 

 

 

name(), toString()

public String name() {
    return name;
}
public String toString() {
    return name;
}

쉽다. name 반환.

 

 

static E valueOf()

  • valueOf() 는 static 메서드이다.
  • 리턴 타입은 enum 상수이다.
  • 매개변수로 String 을 받아서 그 이름과 같은 enum 상수 객체를 리턴한다.
public class ValueOf {
    public static void main(String[] args) {
        Week today = Week.valueOf("FRIDAY");

        System.out.println(today);
        System.out.println(today.getWeekKorean());
    }
}

 

 

static E values()

  • values() 도 static 메서드이다.
  • 해당 enum의 배열을 리턴한다.
public class values {
    public static void main(String[] args) {
        Week[] weeks = Week.values();
        for (Week week : weeks) {
            System.out.println(week.getWeekKorean());
        }
    }
}

 

 

 

values(), valueOf() 가 없다?

  • enum은 Enum 클래스를 상속받아 위와같은 메서드를 쓸 수 있을 것 같지만, 정작 Enum 클래스를 열어보면 이 메서드들이 없다.
  • valueOf() 는 있지만, 메서드 시그니쳐가 다르다.
    public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum constant " + enumType.getCanonicalName() + "." + name);
    }

 

그렇다면 이 메서드들은 어디서 난 것일까.

이 정답도 바이트코드에 있다.

 

  // access flags 0x101A
  private final static synthetic [Lcom/whiteship/white_ship_study/week11/Week; $VALUES

  // access flags 0x9
  public static values()[Lcom/whiteship/white_ship_study/week11/Week;
   L0
    LINENUMBER 3 L0
    GETSTATIC com/whiteship/white_ship_study/week11/Week.$VALUES : [Lcom/whiteship/white_ship_study/week11/Week;
    INVOKEVIRTUAL [Lcom/whiteship/white_ship_study/week11/Week;.clone ()Ljava/lang/Object;
    CHECKCAST [Lcom/whiteship/white_ship_study/week11/Week;
    ARETURN
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x9
  public static valueOf(Ljava/lang/String;)Lcom/whiteship/white_ship_study/week11/Week;
    // parameter mandated  name
   L0
    LINENUMBER 3 L0
    LDC Lcom/whiteship/white_ship_study/week11/Week;.class
    ALOAD 0
    INVOKESTATIC java/lang/Enum.valueOf (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
    CHECKCAST com/whiteship/white_ship_study/week11/Week
    ARETURN
   L1
    LOCALVARIABLE name Ljava/lang/String; L0 L1 0
    MAXSTACK = 2
    MAXLOCALS = 1
  • VALUES 라는 private 인스턴스 변수를 컴파일타임에 자동으로 생성한다.
  • 그리고 선언하지도 않았던 values() 에서 이 인스턴스 변수를 이용하여 메서드를 만들고 있다.
  • 그 아래에는 선언하지 않았던 valueOf() 를 만들고, String 인자를 받아서, Enum 클래스의 valueOf를 자신 객체를 더해서 호출한다.
  • 두 메서드 모두 public static 으로 선언되어 우리가 사용할 수 있는 것이다.

결론 : 모든 enum 타입은 values, valueOf 가 컴파일타임에 자동으로 만들어 진다.

 

 

Clone()

  • Object 클래스는 모든 클래스의 조상이므로 enum 도 clone 이 있을 것이다.
  • 하지만 enum 은 상수로써, private 생성자로 객체생성도 막는데, clone() 를 제공하면 안된다.
  • 그래서 오버라이딩하여 예외를 던지도록 구현해놨더라.
    protected final Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

 

public
class CloneNotSupportedException extends Exception {
    private static final long serialVersionUID = 5195511250079656443L;

    /**
     * Constructs a <code>CloneNotSupportedException</code> with no
     * detail message.
     */
    public CloneNotSupportedException() {
        super();
    }

    /**
     * Constructs a <code>CloneNotSupportedException</code> with the
     * specified detail message.
     *
     * @param   s   the detail message.
     */
    public CloneNotSupportedException(String s) {
        super(s);
    }
}

Exception 을 상속받은 checked 예외다.

 

 

 

Ordinal()

  • 여러개의 상수가 enum에 정의되었다 치자.
  • 각 상수는 정의한 물리적인 순서가 있을 것이다.
  • 이 물리적인 순서를 가져오는 메서드이다.
  • 하지만 이 메서드에는 문제가 있는데, 물리적인 순서는 새로운 상수가 추가되고, 없어지고 함에 따라 바뀔 수 있다는 것이다.
  • 따라서 이 메서드는 사용하면 안되는 메서드이다.
public class OrdinalEx {
    public static void main(String[] args) {
        Week[] weeks = Week.values();
        for (Week week : weeks) {
            System.out.println(week.name() + " : " + week.ordinal());
        }
    }
}

여기서 처음에 다른 날이 추가되면 ? 

 

모든 요일의 순서가 달라졌으므로 출력 순서도 달라진다.

 

 

따라서 순서를 나타내는 필드를 새로 두기도 하는데, 선장님의 팁으로는

순서를 10, 20, 30 처럼 띄엄띄엄 지정해놔야 중간에 새로운 상수가 추가되더라도 순서를 지정하기쉽다.

 

첫출을 보라. 대부분의 프로그래머는 이 메서드를 사용할 일이 없다.

 

EnumSet

  • enum과 함께 사용하기 위한 특별한 Set의 구현체이다.
  • EnumSet 에 정의된 모든 메서드는 static 이다.
  • enum 의 상수들을 Set 으로 만들 수 있는 편리한 기능들을 제공한다.
  • 생성자는 없다.

EnumSet - noneOf(Class<E> elementType)

  • 매개변수로 받은 요소 타입을 사용하여 비어있는 EnumSet을 만든다.
public class EnumSetEx {
    public static void main(String[] args) {
        EnumSet<Week> w = EnumSet.noneOf(Week.class);
        System.out.println(w.size());
    }
}
// 출력값 0

 

EnumSet - AllOf(Class<E> elementType)

  • 매개변수로 받은 요소 타입을 사용하여 모든 상수를 가진 EnumSet을 만든다.
public class EnumSetEx {
    public static void main(String[] args) {
        EnumSet<Week> w = EnumSet.allOf(Week.class);
        w.stream().forEach(System.out::println);
    }
}

 

 

 

 

EnumSet - of(E e1)

  • 어떤 요소가 들어갈지 결정할 수 있다.
  • 5개까지의 인자가 들어갈 수 있도록 오버로딩 되어 있고, 6개부터는 가변인자를 이용하여 오버로딩했다.
  • 제네릭에 선언한 타입의 enum 상수들을 매개변수로 넣는다.
public class EnumSetEx {
    public static void main(String[] args) {
        EnumSet<Week> w = EnumSet.of(Week.FRIDAY, Week.SUNDAY);
        w.stream().forEach(System.out::println);
    }
}

 

 

EnumSet - complementOf(EnumSet<E> s)

  • 매개변수 set 이 포함되지 않은 set을 만든다.
public class Complementof {
    public static void main(String[] args) {
        EnumSet<Week> Monday = EnumSet.of(Week.MONDAY);
        EnumSet<Week> weeks = EnumSet.complementOf(Monday);

        for (Week week : weeks) {
            System.out.println(week);
        }
    }
}

결과는 월요일만 뺀 요일들이 출력된다.

이 메서드는 어떻게 Week의 모든 상수를 알고 거기서 Monday를 빼준걸까.

 

Enum의 인스턴스 변수에는 universe 라는 것이 있다.

이 변수에는 Enum 생성시 enum의 모든 상수가 들어가 있다.

API 설명에는 퍼포먼스 향상을 위한 캐시 데이터라는데, 이런 곳에 쓰이는 것이다.

 

 

EnumSet에는 왜 생성자가 없는가?

  • 원소가 적을 때 사용하는 RegularEnumSet, 원소가 많을 때 사용하는 JumboEnumSet이라는 구현체들이 존재하고, 이 클래스에서 상수의 개수에 따라 다른 구현체로 객체를 생성하게 할 수 있다.
  • 원소의 초기화를 간편하게 할 수 있다.
  • RegularEnumSet, JumboEnumSet 이외에 다른 구현체가 새로 추가된다 하여도, 기존 클라이언트 코드에는 영향을 주지 않는다. 확장성이 좋다는 말이다.

 

 

 

 

마지막 - enum의 장점

  • IDE의 지원을 받을 수 있다 - 자동완성, 오타검증 등
  • 값을 제한할 수 있다 - 예를 들어 매개변수로 특정한 객체들만 들어갈 수 있을 떄, enum을 사용하면 enum에 정의되지 않은 상수는 컴파일타임에 오류를 표출한다.
  • 리펙토링이 좋다 - 상수 및 생성자, 메서드를 모아 놓았으므로 리펙터링시 해당 enum만 변경하면 된다.

 

 

 

 

마지막의 마지막 - enum 활용의 하나

  • abstract 메서드를 선언하므로써 상수 객체마다 각자의 동작을 할 수 있게 만들 수 있다.
  • 람다식과 함수형 인터페이스를 이용해서도 가능한데, 저번에 작성한 나의 글을 첨부한다.

alkhwa-113.tistory.com/entry/Enum-%ED%99%9C%EC%9A%A9%EB%9E%8C%EB%8B%A4%EC%8B%9D-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

 

 

 

 

참고 :

effective Java 3E

parkadd.tistory.com/50

www.notion.so/Enum-6ffa87530c424d8ab7a1b585bfb26fa2

 

 

 

 

'WhiteShip Java Study : 자바 처음부터 멀리까지' 카테고리의 다른 글

ServiceLoader  (0) 2021.02.14
Annotation  (0) 2021.02.13
멀티스레드 프로그래밍  (0) 2021.02.06
예외 처리  (0) 2021.02.05
인터페이스  (0) 2021.02.04