JAVA - enum

mseo39·2023년 11월 6일
0

우아한테크코스

목록 보기
2/2

생활코딩 - enum 강의를보고 공부한 내용입니다

Java - 상수와 enum

상수란 변하지 않는 값을 말한다.

📂 다음 코드를 보면 사과, 복숭아, 바나나가 숫자에 의미가 담겨있고 주석으로 각 숫자가 어떤 과일을 의미하는지 전달하고 있다. 이렇게 코드를 작성했을 때, 주석이 없어지거나 주석이 다른 곳에 작성된다면 제 3자 입장에서도 수정해야하는 개발자입장에서도 의미를 파악할 수 없어서 곤란하다.

package org.opentutorials.javatutorials.constant2;
 
public class ConstantDemo {
    public static void main(String[] args) {
        /*
         * 1. 사과
         * 2. 복숭아
         * 3. 바나나
         */
        int type = 1;
        switch(type){
            case 1:
                System.out.println(57);
                break;
            case 2:
                System.out.println(34);
                break;
            case 3:
                System.out.println(93);
                break;
        }
    }
 
}

🔍 그래서 다음과 같이 수정한다. 숫자가 아니라 이름으로 한다면 저 좋을 것이다. 그래서 변수를 지정하고 그 변수를 final로 처리하면 한번 설정된 값은 바뀌지 않는다. 또한 바뀌지 않는 값이라면 인스턴스 변수가 아니라 클래스 변수로 설정해준다.
-> 주석이 없더라도 변수만 봐도 어떤 것을 의미하는지 알 수 있게 된다

package org.opentutorials.javatutorials.constant2;
 
public class ConstantDemo {
    private final static int APPLE = 1; <- 추가
    private final static int PEACH = 2; <- 추가
    private final static int BANANA = 3; <- 추가
    public static void main(String[] args) {
        int type = APPLE;
        switch(type){
            case APPLE:
                System.out.println(57+" kcal");
                break;
            case PEACH:
                System.out.println(34+" kcal");
                break;
            case BANANA:
                System.out.println(93+" kcal");
                break;
        }
    }
}

Java - enum의 배경

📂 그러다가 이 프로그램에서 기업에 대한 상수도 필요해져서 다음과 코드를 작성했다

package org.opentutorials.javatutorials.constant2;
 
public class ConstantDemo {
    // fruit
    private final static int APPLE = 1;
    private final static int PEACH = 2;
    private final static int BANANA = 3;
     
    // company
    private final static int GOOGLE = 1;
    //private final static int APPLE = 2;
    private final static int ORACLE = 3;
     
    public static void main(String[] args) {
        int type = APPLE;
        switch(type){
            case APPLE:
                System.out.println(57+" kcal");
                break;
            case PEACH:
                System.out.println(34+" kcal");
                break;
            case BANANA:
                System.out.println(93+" kcal");
                break;
        }
    }
}

🔍 위와 같이 작성했을 때 과일과 기업의 이름이 겹치게되어 프로그램이 오작동한다. 그래서 변수명에 접두사를 붙이는 방식으로 문제를 해결해보았다

package org.opentutorials.javatutorials.constant2;
 
public class ConstantDemo {
    // fruit
    private final static int FRUIT_APPLE = 1; <- 수정
    private final static int FRUIT_PEACH = 2; <- 수정
    private final static int FRUIT_BANANA = 3; <- 수정
     
    // company
    private final static int COMPANY_GOOGLE = 1; <- 수정
    private final static int COMPANY_APPLE = 2; <- 수정
    private final static int COMPANY_ORACLE = 3; <- 수정
     
    public static void main(String[] args) {
        int type = FRUIT_APPLE;
        switch(type){
            case FRUIT_APPLE:
                System.out.println(57+" kcal");
                break;
            case FRUIT_PEACH:
                System.out.println(34+" kcal");
                break;
            case FRUIT_BANANA:
                System.out.println(93+" kcal");
                break;
        }
    }
}

🔍 그런데 변수명이 너무 지저분하기 때문에 인터페이스를 사용하여 깔끔하게 정리할 수 있다.

package org.opentutorials.javatutorials.constant2;
 
interface FRUIT{ <- 추가
    int APPLE=1, PEACH=2, BANANA=3;
}
interface COMPANY{ <- 추가
    int GOOGLE=1, APPLE=2, ORACLE=3;
}
 
public class ConstantDemo {
     
    public static void main(String[] args) {
        int type = FRUIT.APPLE; <- 수정
        switch(type){
            case FRUIT.APPLE: <- 수정
                System.out.println(57+" kcal");
                break;
            case FRUIT.PEACH: <- 수정
                System.out.println(34+" kcal");
                break;
            case FRUIT.BANANA: <- 수정
                System.out.println(93+" kcal");
                break;
        }
    }
}

🔍 하지만 이렇게 작성했을 때 다음과 같은 문제가 생길 수 있다. 누군가 과일관련 변수와 기업관련 변수를 비교하는 코드를 실행한다고 한다. 이것은 과일과 기업을 비교하는 잘못된 코드임에도 불구하고 컴파일러는 이 오류를 찾지 못하고 동작시킨다. 그 이유는 둘의 변수명이 int로 같기 때문에 비교를 할 수 있는 것이다. 우리는 이것을 방지하기 위해서 클래스를 만들어준다.

-> 이렇게 함으로써 과일과 기업은 서로 다른 그룹의 상수이기 때문에 Fruit.APPLE == Company.APPLE은 동작하지 않게되고 컴파일 시에 오류가 검출될 수 있게 되었다

package org.opentutorials.javatutorials.constant2;
 
class Fruit{ <- 수정
    public static final Fruit APPLE  = new Fruit();
    public static final Fruit PEACH  = new Fruit();
    public static final Fruit BANANA = new Fruit();
}
class Company{ <- 수정
    public static final Company GOOGLE = new Company();
    public static final Company APPLE = new Company();
    public static final Company ORACLE = new COMPANY(Company);
}
 
public class ConstantDemo {
     
    public static void main(String[] args) {
        if(Fruit.APPLE == Company.APPLE){
            System.out.println("과일 애플과 회사 애플이 같다.");
        }
    }
}

하지만 위의 코드는 다음과 같은 단점을 가지고 있다.
1. switch문에서 사용할 수 없다(몇가지 제한된 데이터 타입만을 사용할 수 있다)
2. 하나의 선언이 복잡하다

Java - enum의 문법

enum은 열거형이라고 부른다. 열거형은 서로 연관된 상수들의 집합이라고 할 수 있다. 위의 예제에서 과일과 기업이 열거인 셈이다
java에서는 이것을 문법적으로 지원하는게 그것이 바로 enum이다

package org.opentutorials.javatutorials.constant2;
 
enum Fruit{ <- 수정
    APPLE, PEACH, BANANA;
}
enum Company{ <- 수정
    GOOGLE, APPLE, ORACLE;
}
 
public class ConstantDemo {
     
    public static void main(String[] args) {
        /*
        if(Fruit.APPLE == Company.APPLE){
            System.out.println("과일 애플과 회사 애플이 같다.");
        }
        */
        Fruit type = Fruit.APPLE;
        switch(type){
            case APPLE:
                System.out.println(57+" kcal");
                break;
            case PEACH:
                System.out.println(34+" kcal");
                break;
            case BANANA:
                System.out.println(93+" kcal");
                break;
        }
    }
}

🔍 enum은 사실상 class이며 enum만의 문법적 형식을 가지고 있기 때문에 enum 키워드를 사용한다. 아래 두개의 코드는 똑같은 역할을 하고 있다. 하지만 둘을 비교하면 enum을 사용한 코드가 훨씬 깔끔하다는 것을 알 수 있다. 또 enum도 서로 다른 상수 그룹에 대한 비교를 컴파일 시점에서 차단할 수 있다.

📃 enum을 사용한 코드
enum Fruit{
    APPLE, PEACH, BANANA;
}

📃 class를 사용한 코드
class Fruit{
    public static final Fruit APPLE  = new Fruit();
    public static final Fruit PEACH  = new Fruit();
    public static final Fruit BANANA = new Fruit();
    private Fruit(){}
}

🔍 enum을 사용하는 이유를 정리하면 다음과 같다
1. 코드가 단순해진다
2. 인스턴스 생성과 상속을 방지한다
3. 키워드 enum을 사용하기 때문에 구현의 의도가 열거임을 분명하게 나타낼 수 있다

Java - 열거형의 활용

📂 enum은 클래스기 때문에 enum을 어떻게 활용할 수 있을지 알아보자

🔍 Call Constructor가 세번 출력된다. 이것을 보아 필드의 숫자만큼 생성자가 호출되었다는 것을 알 수 있다. 즉 위에서 봤던 class를 사용한 코드와 똑같이 작동한다는 것도 확인할 수 있다
-> 또한 enum의 생성자가 접근 제어자 private만을 허용하기 때문에 직접 생성할 수는 없다

package org.opentutorials.javatutorials.constant2;
 
enum Fruit{
    APPLE, PEACH, BANANA;
    Fruit(){  <- 추가
        System.out.println("Call Constructor "+this);
    }
}
 
enum Company{
    GOOGLE, APPLE, ORACLE;
}
 
public class ConstantDemo {
     
    public static void main(String[] args) {
     
        /*
        if(Fruit.APPLE == Company.APPLE){
            System.out.println("과일 애플과 회사 애플이 같다.");
        }
        */
        Fruit type = Fruit.APPLE;
        switch(type){
            case APPLE:
                System.out.println(57+" kcal");
                break;
            case PEACH:
                System.out.println(34+" kcal");
                break;
            case BANANA:
                System.out.println(93+" kcal");
                break;
        }
    }
}

🔍 그러면 enum은 생성자의 매개변수를 통해 인스턴스 변수값을 부여할 수 없을까? 다음 코드와 같이 작성하여 부여할 수 있다

enum Fruit{
    APPLE("red"), PEACH("pink"), BANANA("yellow");  <- 수정
    public String color; <- 추가
    Fruit(String color){  <- 수정
        System.out.println("Call Constructor "+this);
        this.color = color;
    }
}

public class ConstantDemo {
     
    public static void main(String[] args) {
        /*
        if(Fruit.APPLE == Company.APPLE){
            System.out.println("과일 애플과 회사 애플이 같다.");
        }
        */
        Fruit type = Fruit.APPLE;
        switch(type){
            case APPLE:
                System.out.println(57+" kcal, "+Fruit.APPLE.color); <- 수정
                break;
            case PEACH:
                System.out.println(34+" kcal"+Fruit.PEACH.color); <- 수정
                break;
            case BANANA:
                System.out.println(93+" kcal"+Fruit.BANANA.color); <- 수정
                break;
        }
    }
}

🔍 enum은 멤버 전체를 열거할 수 있는 기능도 제공한다

package org.opentutorials.javatutorials.constant2;
 
enum Fruit{
    APPLE("red"), PEACH("pink"), BANANA("yellow");
    private String color;
    Fruit(String color){
        System.out.println("Call Constructor "+this);
        this.color = color;
    }
    String getColor(){  <- 추가
        return this.color;
    }
}
 
enum Company{
    GOOGLE, APPLE, ORACLE;
}
 
public class ConstantDemo {
     
    public static void main(String[] args) {
        for(Fruit f : Fruit.values()){  <- 추가
            System.out.println(f+", "+f.getColor());
        }
    }
}

열거형의 특성을 정리해보면 열거형은 연관된 값들을 저장한다. 또 그 값들이 변경되지 않도록 보장한다. 뿐만 아니라 열거형 자체가 클래스이기 때문에 열거형 내부에 생성자, 필드, 메소드를 가질 수 있어서 단순히 상수가 아니라 더 많은 역할을 할 수 있다.

0개의 댓글