<aside> 😅 Enum? 열거형은 서로 관련된 상수를 편리하게 선언하기 위한 것으로 상수를 여러 개 정의할 때 사용한다. 열거형은 상수 정의 이후 정의된 것 이외의 값은 허용하지 않는다. 오라클 튜토리얼에는 열거형을 이렇게 설명하고 있다. 열거형 은 변수가 미리 정의된 상수 집합이 될 수 있도록 하는 특수 데이터 유형입니다 . 변수는 변수에 대해 미리 정의된 값 중 하나와 같아야 합니다. 즉, 열거형은 상수 집합을 만들때 사용한다는 것이다. 예를 들자면 뭐가 있을까? 바뀌지 않지만 집합의 형태를 가지고 있는 우리 일상 속의 것들이? 요일, 달력의 날짜, 국가명, RGB값? 이런것도 될 수 있지 않을까?

</aside>

public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY 
}
...
public class EnumTest {
    Day day;
    
    public EnumTest(Day day) {
        this.day = day;
    }
    
    public void tellItLikeItIs() {
        switch (day) {
            case MONDAY:
                System.out.println("Mondays are bad.");
                break;
                    
            case FRIDAY:
                System.out.println("Fridays are better.");
                break;
                         
            case SATURDAY: case SUNDAY:
                System.out.println("Weekends are best.");
                break;
                        
            default:
                System.out.println("Midweek days are so-so.");
                break;
        }
    }
    
    public static void main(String[] args) {
        EnumTest firstDay = new EnumTest(Day.MONDAY);
        firstDay.tellItLikeItIs();
        EnumTest thirdDay = new EnumTest(Day.WEDNESDAY);
        thirdDay.tellItLikeItIs();
        EnumTest fifthDay = new EnumTest(Day.FRIDAY);
        fifthDay.tellItLikeItIs();
        EnumTest sixthDay = new EnumTest(Day.SATURDAY);
        sixthDay.tellItLikeItIs();
        EnumTest seventhDay = new EnumTest(Day.SUNDAY);
        seventhDay.tellItLikeItIs();
    }
}

CONSOLE RESULT
Mondays are bad.
Midweek days are so-so.
Fridays are better.
Weekends are best.
Weekends are best

Enum의 정의 방법

<aside> 😅 열거형 내부에는 메서드, 기타 필드가 포함될 수 있다. 단, 컴파일러는 열거형을 만들때 values(), valueOf() 추가해서 만들어준다. 예를 들자면 다음과 같다. ***- values() : 선언된 순서대로 열거형의 모든 값을 포함하는 배열을 반환하는 메서드 컴파일하면서 생긴다.

</aside>

<aside> 📌 ordinal()? 문제가 있다. 이걸 기반해서 코드를 작성하면 위험하다. 왜냐하면 0~… int를 반환하는데 순서가 바뀌면 위험할 수 있다. 대부분의 프로그래머들은 이걸 쓸 일 없다. 내부용이다.

</aside>

public enum Planet {
    MERCURY (3.303e+23, 2.4397e6),
    VENUS   (4.869e+24, 6.0518e6),
    EARTH   (5.976e+24, 6.37814e6),
    MARS    (6.421e+23, 3.3972e6),
    JUPITER (1.9e+27,   7.1492e7),
    SATURN  (5.688e+26, 6.0268e7),
    URANUS  (8.686e+25, 2.5559e7),
    NEPTUNE (1.024e+26, 2.4746e7);

    private final double mass;   // in kilograms
    private final double radius; // in meters

    Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
    }
    private double mass() { return mass; }
    private double radius() { return radius; }

    // universal gravitational constant  (m3 kg-1 s-2)
    public static final double G = 6.67300E-11;

    double surfaceGravity() {
        return G * mass / (radius * radius);
    }
    double surfaceWeight(double otherMass) {
        return otherMass * surfaceGravity();
    }
    public static void main(String[] args) {
        if (args.length != 1) {
            System.err.println("Usage: java Planet <earth_weight>");
            System.exit(-1);
        }
        double earthWeight = Double.parseDouble(args[0]);
        double mass = earthWeight/EARTH.surfaceGravity();
        for (Planet p : Planet.values())
           System.out.printf("Your weight on %s is %f%n",
                             p, p.surfaceWeight(mass));
    }
}

<aside> 😅 이렇게 보면 Enum을 왜 만들었나 싶다. 당근 classstatic final로 상수를 정의하면 될 것 같은데 왜 만든 것일까?필자가 찾아보고 개인적인 견해에 의해서 적어본다면 데이터의 추상적인 부분을 가시적으로 만들어주기 위함이라고 생각한다. 예를 들자면 맥북을 판매하는 서비스를 만든다고 가정해보자. 그럼 다음과 같은 코드를 만들 수 있다.

</aside>

public class Test{
    static class MacBook{
        public static final int MAC_13= 1_000_000;
        public static final int MAC_16= 2_000_000;
    }

    public static void main(String[] args) {
        int price =getPrice(MacBook.MAC_13); // 100만원 입니다. 출력
    }

    public static int getPrice(int price){
        int type = price;
        switch (type) {
            case MacBook.MAC_13:
                System.out.println("100만원 입니다.");
                return MacBook.MAC_13;
            case MacBook.MAC_16:
                System.out.println("200만원 입니다.");
                return MacBook.MAC_16;
            default:
                throw new NullPointerException("가격정보가 없습니다");
        }
    }
}

<aside> 😅 하지만 이것은 맥북이 인텔 맥북만 존재할때 이야기였다. 그런데 맥북에 m1 맥북이 추가되어 우리는 상수를 고쳐야할 일이 생겨버렸다. 하지만 인텔맥과 m1 맥이 동일한 가격 동일한 성능으로 출시되었기 때문에 이렇게 이름을 바꿔줄 수 밖에 없었다. 그런데 이렇게 하면 동일한 맥북인데 이름을 다르게 할 수 밖에 없었다. 또한 이렇게 하면 개발자의 실수를 부추기는 코드가 되어버린다. 그 예시는 아래 첫번째 코드와 같다. 다음 문제는 아주 심각한 문제를 일으킨다. 다른 메서드에 개념적으로 다른 어떠한 값을 집어넣어도 type이 동일하기 때문에 컴퓨터는 알아차리지 못한다(아까 언급한 추상적인 부분이다). 이 문제를 해결하기 위해서 아래의 두번째 코드와 같이 인터페이스를 사용하면 되지만 그건 안티패턴이다. 그럼 어떻게 해야할까?

</aside>

static class MacBook{
    public static final int MAC_13 = 1_000_000;
    public static final int MAC_16 = 2_000_000;

    public static final int M1_MAC_13 = 1_000_000;
    public static final int M1_MAC_16 = 2_000_000;
}
...
public class Test{
    static class MacBook{
        public static final int MAC_13 = 1_000_000;
        public static final int MAC_16 = 2_000_000;

        public static final int M1_MAC_13 = 1_000_000;
        public static final int M1_MAC_16 = 2_000_000;
    }
    public static void main(String[] args) {
        int price = getIntelMacPrice(MacBook.M1_MAC_13);  
				**// 인텔맥의 가격을 가져오는 메서드에 M1맥을 집어 넣어도 실행이 되어버린다.
        // 내가 원하는 것은 인텔맥13형인데?.**
    }

    public static int getIntelMacPrice(int price){
        int type = price;
        switch (type) {
            case MacBook.MAC_13:
                System.out.println("100만원 입니다.");
                return MacBook.MAC_13;
            case MacBook.MAC_16:
                System.out.println("200만원 입니다.");
                return MacBook.MAC_16;
            default:
                throw new NullPointerException("가격정보가 없습니다");
        }
    }
}
interface IntelMac{
    public static final int MAC_13 = 1_000_000;
    public static final int MAC_16 = 2_000_000;
}

interface M1Mac{
    public static final int MAC_13 = 1_000_000;
    public static final int MAC_16 = 2_000_000;
}

<aside> 😅 이런 이유에서 Enum이 탄생하게 되었다. 위의 코드를 Enum Type으로 수정하면 다음과 같이 코드를 만들 수 있다. 조금 다른점은 이제는 Enum M1Mac에 정의된 MAC_13형을 넣으면 컴파일러가 잡아준다.

Untitled

</aside>

public class Test{
    enum IntelMac{
        MAC_13(1_000_000),
        MAC_16(2_000_000);

        private int price;
        IntelMac(int price) {
            this.price = price;
        }

        public int getPrice(){
            return price;
        }

    }
    enum M1Mac{
        MAC_13(1_000_000),
        MAC_16(2_000_000);

        private int price;

        M1Mac(int price) {
            this.price = price;
        }

        public int getPrice(){
            return price;
        }
    }

    public static void main(String[] args) {
        int price = getIntelMacPrice(IntelMac.MAC_13);
    }

    public static int getIntelMacPrice(IntelMac mac){
        switch (mac) {
            case MAC_13:
                System.out.println("100만원 입니다.");
                return IntelMac.MAC_13.getPrice();
            case MAC_16:
                System.out.println("200만원 입니다.");
                return IntelMac.MAC_16.getPrice();
            default:
                throw new NullPointerException("가격정보가 없습니다");
        }
    }
}

<aside> 😅 이렇듯 Enum은 서로 다른 개념의 충돌할때 개발자가 그걸 가시적으로 볼 수 있는 기회를 제공해준다.

</aside>


Enum 특징

<aside> 😅 Enum의 특징은 엄연한 Type이라는 점이다(Type이 존재한다는건 특성 - 객체라는 의미와도 같다고 생각한다). 즉, Enum도 클래스다. 이 외에도 몇가지 특징을 나열하면 다음과 같다.

</aside>