티스토리 뷰

JAVA/JAVA기본

JAVA - Enum이란?

realizers 2022. 1. 29. 14:16
728x90
반응형

Enum이란?


  • Enum이란 열거형이라고 하며 서로 연관된 상수들의 집합을 의미합니다.

 

Enum의 등장배경


  • Enum의 등장 전에는 어떠한 상수를 선언 시 static final으로 다 선언하자니 네임충돌이 발생할 우려도 있고 복잡했습니다.
  • 인터페이스를 사용하면 위의 문제는 해결되나 타입의 안정성이 떨어지게 되었습니다.

 

static final을 사용하여 변수 선언
  • 아래 예제를 살펴보면 각각의 상수에 부여된 1, 2, 3이라는 리터럴은 단순히 상수들을 구분하고 이용하기 위해 부여된 값들이지 어떠한 논리적인 의미가 없습니다. 다시 말해 APPLE이 1인 의미는 굳이 1일 필요가 없다는 것입니다.
  • 또한 네임충돌이 발생할 수 있습니다. 과일의 APPLE과 회사의 APPLE이 있다면 각각 구분을 해주어야 합니다.
public class EnumExample {
    public static final int FRUIT_APPLE = 1;
    public static final int FRUIT_PEACH = 2;
    public static final int FRUIT_BANANA = 3;

    public static final int COMPANY_APPLE = 1;
    public static final int COMPANY_GOOGLE = 2;
    public static final int COMPANY_FACEBOOK = 3;


    public static void main(String[] args) {
        int type = FRUIT_PEACH;

        switch (type) {
            case FRUIT_APPLE:
                System.out.println("This is APPLE");
                break;
            case FRUIT_PEACH:
                System.out.println("This is PEACH");
                break;
            case FRUIT_BANANA:
                System.out.println("This is BANANA");
                break;
        }
    }
}

 

인터페이스를 사용하여 변수 선언
  • 인터페이스를 사용하게 된다면 위의 문제를 해결할 수는 있으나 인터페이스는 규약을 정하기 위해 만들어 진것이지 이런식으로 사용하라고 만들어진 개념이 아닙니다.
  • Fruit.APPLE과 Company.APPLE는 모두 같은 INT 타입의 자료형이기 때문에 if 문을 사용하여 비교를 할 수 있습니다. 하지만 여기서 과일과 회사는 서로 비교할 수 있을까요? 당연히 서로 비교되어서는 안되는 개념입니다. 따라서 if문을 사용하여 비교조차할 수 없도록 컴파일 과정에서 막아주는 것이 좋습니다.
public interface Fruit {
    int APPLE = 1;
    int PEACH = 2;
    int BANANA = 3;
}

public interface Company {
    int APPLE = 1;
    int GOOGLE = 2;
    int FACEBOOK = 3;
}

public class EnumExample {
    public static void main(String[] args) {
       if (Fruit.APPLE == Company.APPLE) {
           ...
       }
    }
}

 

기존 인터페이스를 클래스로 변경
  • 위의 문제에서 인터페이스를 사용하게 되면 컴파일 시점에서 체크를 할 수 없었습니다. 그래서 클래스형으로 변경을 하게되면 Company와 Fruit 객체가 애초에 비교를 하지 못하도록 객체를 만들어주면 모든 문제가 해결되게 됩니다.
    • 상수와 리터럴이 논리적인 연관이 없습니다.
    • 서로 다른 개념끼리 네임충돌이 발생할 수 있습니다.
    • 서로 다른 개념끼리 비교하는 코드를 작성할 수 있습니다.
    • 이러한 3가지의 문제점을 바로 잡을 수 있습니다.
  • 하지만 Enum을 사용하게 된다면 구구절절했던 문제들을 간단히 잡을 수 있습니다.
public class Company {
    public static final Company APPLE = new Company();
    public static final Company GOOGLE = new Company();
    public static final Company FACEBOOK = new Company();
}

public class Fruit {
    public static final Fruit APPLE = new Fruit();
    public static final Fruit PEACH = new Fruit();
    public static final Fruit BANANA = new Fruit();
}

public class Example {
    public static void main(String[] args) {

        // 컴파일 에러 발생
        if (Fruit.APPLE == Company.APPLE) {

        }
    }
}

 

Enum을 사용하여 변경
  • 이렇게 Enum을 사용하게 된다면 컴파일 시점에서 막아줄 뿐아니라 코드의 간결성을 높일 수 있습니다.
public enum Company {
    APPLE,
    GOOGLE,
    FACEBOOK;
}

public enum Fruit {
    APPLE,
    PEACH,
    BANANA;
}

public class Example {
    public static void main(String[] args) {

        // 컴파일 에러 발생
        if (Fruit.APPLE == Company.APPLE) {

        }

        // 같은 열거형이므로 컴파일 에러 발생하지 않음
        if (Fruit.APPLE == Fruit.BANANA) {
            
        }
    }
}

 

Enum의 정의하는 방법


enum의 생성자는 왜 private일까?
  • 자바에서 enum 타입은 열거형을 의미하는 특별한 형태의 클래스입니다. 그렇기 때문에 일반 클래스와 같이 생성자가 있어야 합니다. 물론 생성자가 없어도 컴파일러가 기본 생성자를 만들어 주긴 하지만 enum의 경우에는 생성자의접근 제어자를 private로 지정합니다. 
  • enum 타입은 고정된 상수들의 집합으로써 런타임 시점이 아닌 컴파일 시점에 모든 값을 알고 있어야합니다. 즉 다른 패키지나 클래스에서 enum의 타입에 접근하여 어떠한 값도 변경을 할 수 없습니다.
public enum Fruit {
    APPLE(1),
    PEACH(2),
    BANANA(3);

    private int value;

    Fruit(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}

public class EnumExample {
    public static void main(String[] args) {
        System.out.println(Fruit.BANANA.getValue()); // 3
    }
}

 

Enum의 특징


  • 열거형으로 선언된 순서에 따라 0부터 index값을 가집니다.(순차적으로 증가)
  • enum 열거형으로 지정된 상수들은 모두 대문자로 선언합니다.
  • 열거형 변수들은 선언한 후 마지막에 세미콜론을 찍지 않습니다.
  • 상수와 특정 값을 연결시킬 경우 마지막에 세미콜론을 붙여주어야 합니다.

 

Enum이 제공하는 메서드


values()
  • 열거 타입의 모든 열거 객체들을 배열로 만들어 반환합니다.
public enum Fruit {
    APPLE(1),
    PEACH(2),
    BANANA(3);

    private int value;

    Fruit(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}

public class EnumExample {
    public static void main(String[] args) {
        for (Fruit item : Fruit.values()) {
            System.out.println(item);
        }
    }
}

 

valueOf()
  • 매개값으로 주어진 문자열과 동일한 문자열을 가지는 열거 객체를 반환합니다.
public enum Fruit {
    APPLE(1),
    PEACH(2),
    BANANA(3);

    private int value;

    Fruit(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}

public class EnumExample {
    public static void main(String[] args) {
       Fruit fruit = Fruit.valueOf("PEACH");
        System.out.println(fruit);
        System.out.println(fruit.getValue());
    }
}

 

ordinal()
  • 전체 열거 객체 중 몇번째 열거 객체인지 알려줍니다. 열거 객체의 순번은 0부터 시작됩니다.
public enum Fruit {
    APPLE(1),
    PEACH(2),
    BANANA(3);

    private int value;

    Fruit(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}

public class EnumExample {
    public static void main(String[] args) {
       Fruit fruit = Fruit.PEACH;
        System.out.println(fruit.ordinal()); // 1
    }
}

 

java.lang.Enum


  • 모든 enum은 내부적으로 java.lang.enum 클래스에 의해 상속됩니다. 자바는 다중 상속을 허용하지 않기 때문에 enum은 다른 클래스를 상속 받을 수 없습니다.

 

EnumSet


  • EnumSet 열거형을 위해 고안된 특별한 Set 인터페이스 구현체입니다. HashSet과 비교했을 때 성능상의 이점이 많기 때문에 열거형 데이터를 위한 Set이 필요한 경우 EnumSet을 사용하는 것이 좋습니다.
  • 기본적으로 EnumSet은 abstract 키워드가 사용되기 때문에 객체로써 생성 및 사용이 불가능 합니다.
allOf() - 모든 요소를 포함하는 EnumSet
public enum Fruit {
    APPLE,
    PEACH,
    BANANA;
}

public class EnumExample {
    public static void main(String[] args) {
        EnumSet enumSet = EnumSet.allOf(Fruit.class);

        enumSet.forEach(item -> System.out.println(item));
    }
}

 

complementOf() - 매개변수로 전달된 요소를 제외합니다.
public enum Fruit {
    APPLE,
    PEACH,
    BANANA;
}

public class EnumExample {
    public static void main(String[] args) {
        EnumSet enumSet = EnumSet.complementOf(EnumSet.of(Fruit.APPLE));

        enumSet.forEach(item -> System.out.println(item));
    }
}

noneOf() - 비어있는 EnumSet 반환
of() - 들어갈 요소를 직접 입력하여 EnumSet을 생성
728x90
반응형