이번 자바 개인 과제(키오스크 도전 Lv.2)는 진행하는데 있어서 크게 어려움을 겪진 않았던 것 같다. 하지만 Enum에 대해 새롭게 알게 된 내용, 궁금했던 내용들이 있어서 그것들을 정리해두려고 한다. enum에 대해 궁금했던 내용들은 다음과 같다.
- 저번 과제(계산기)를 할 때 계산하는 메소드를 추상 메소드로서 상속(
extends
) 받아 사용하려고 했더니 사용할 수 없다고 빨간 줄이 나타났다. 그래서 인터페이스를 구현(implements
)하는 방법을 사용했는데, 왜 enum에서 상속(extends
)은 사용할 수 없을까?- enum에서 상속을 사용할 수 없다면 추상 메소드도 사용할 수 없는 것인가?
- enum에서 제공하는 메소드 중에
values()
는 어떻게 구현되어 있는지 볼 수 없는데 어떻게 사용할 수 있는 것인가?
열거형이라고도 부르며 서로 관련이 있는 상수들의 집합을 의미한다.
- 코드의 가독성을 높이고 논리적인 오류를 줄일 수 있다.
- 서로 다른 개념이지만 이름이 같아서 충돌할 수 있는 문제를 해결할 수 있다.
- 예를 들어 APPLE은 먹는 사과가 될 수도 있고 회사가 될 수도 있다. 이런 경우 Fruit.APPLE과 Company.APPLE로 정의한다면 구분이 가능해진다.
- 수정 범위가 최소화된다.
- enum에 정의된 상수들은 enum 타입의 객체이다.
- 생성자와 메소드를 추가할 수 있다.
- 상수의 이름만 선언하는게 아닌 경우에는 마지막에 세미콜론을 붙여줘야 한다.
- class 키워드를 통해서 생성하지는 않지만 enum도 클래스이다.
- 명시적으로 표시하지 않아도 컴파일러에 의해 상수 각각의 객체가 만들어지고
public static final
을 갖게되며, 생성자의 접근제어자는private
이 된다.
- 외부에서 인스턴스를 생성하거나 변경할 수 없게 하여, 상수 집합이 불변하고 일관성 있게 유지되도록 하기 위함이다.
public enum Fruit { APPLE, BANANA, ORANGE, MANGO; // 생성자를 추가했기 때문에 마지막에 세미콜론을 붙여줘야 한다. Fruit() { // 생성자 코드 } } 👇 컴파일 후 public enum Fruit { public static final Fruit APPLE = new Fruit(); public static final Fruit BANANA = new Fruit(); public static final Fruit ORANGE = new Fruit(); public static final Fruit MANGO = new Fruit(); private Fruit() { // 생성자 코드 } }
- enum 상수 간의 비교가 가능하다.
- enum 상수는 고유한 객체로 취급되기 때문에
==
를 사용하여 참조 비교를 할 수 있다.- enum은 자체적으로 크기 비교를 하지 않기 때문에
>
나<
를 사용하여 비교할 수 없다.- 대신
ordinal()
,compareTo()
를 사용하여 순서 비교를 할 수 있다.- switch문의 조건식에도 들어갈 수 있다.
- 인터페이스를 구현(implements)할 수는 있지만, 클래스를 상속(extends) 받을 수는 없다.
- 컴파일 시점에 컴파일러에 의해
extends Enum
이 추가된다.- 다중 상속을 지원하지 않기 때문에 enum에는
extends
를 사용할 수 없다.
- values()
해당 enum 타입에 정의된 모든 상수들을 배열로 반환한다. values()는 어떻게 구현되어 있는지 볼 수 없는데 컴파일 시점에 컴파일러에 의해 삽입되기 때문이다.
Java programming language enum types are much more powerful than their counterparts in other languages. The enum declaration defines a class (called an enum type). The enum class body can include methods and other fields. The compiler automatically adds some special methods when it creates an enum. For example, they have a static values method that returns an array containing all of the values of the enum in the order they are declared. This method is commonly used in combination with the for-each construct to iterate over the values of an enum type.
자바 프로그래밍 언어의 enum 타입은 다른 언어의 enum 타입보다 훨씬 더 강력합니다. enum 선언은 클래스를 정의합니다(이를 enum 타입이라고 부릅니다). enum 클래스의 본문에는 메소드와 다른 필드를 포함할 수 있습니다. 컴파일러는 enum을 생성할 때 자동으로 몇 가지 특수한 메소드를 추가합니다. 예를 들어, enum 타입에는 선언된 순서대로 모든 값을 포함하는 배열을 반환하는 static values 메소드가 있습니다. 이 메소드는 일반적으로 enum 타입의 값을 순회할 때 for-each 문과 함께 사용됩니다.
https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html
for (Fruit fruit : Fruit.values()) { System.out.println(fruit); }
- valueOf()
이름이 일치하는 enum 상수를 반환한다.public enum Fruit { APPLE, BANANA, ORANGE, MANGO; } public static void main(String[] args) { // Enum.valueOf(Class<T> enumType, String name) // Enum의 클래스 타입이 매개변수이기 때문에 제네릭과 함께 사용한다면 유연하게 동작하는 메소드를 작성할 수 있다. Fruit apple = Enum.valueOf(Fruit.class, "APPLE"); // valueOf(String name) Fruit banana = Fruit.valueOf("BANANA"); }
public enum Fruit { APPLE, BANANA, ORANGE, MANGO; } public enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY; } public static <E extends Enum<E>> E getEnumConstant(Class<E> enumClass, String constantName) { return Enum.valueOf(enumClass, constantName); } // Enum.valueOf()를 사용해서 다양한 enum 상수를 가져오는 예 public static void main(String[] args) { Day monday = getEnumConstant(Day.class, "MONDAY"); System.out.println(monday); // MONDAY Fruit banana = getEnumConstant(Fruit.class, "BANANA"); System.out.println(banana); // BANANA }
- ordinal()
enum 상수가 정의된 순서를 반환한다. (0부터 시작)public enum Fruit { APPLE, BANANA, ORANGE, MANGO; } public static void main(String[] args) { System.out.println(Fruit.APPLE.ordinal()); // 0 System.out.println(Fruit.BANANA.ordinal()); // 1 System.out.println(Fruit.ORANGE.ordinal()); // 2 System.out.println(Fruit.MANGO.ordinal()); // 3 }
- name()
enum 상수의 이름을 문자열로 반환한다.- getDeclaringClass()
enum 상수나 클래스에 대해 호출하여 해당 상수가 선언된 클래스를 반환한다.
extends
로 추상 클래스를 상속 받을 수 없어도 추상 메소드를 사용할 수 있다.
- 추상 메소드를 사용하면 enum 자체가 암묵적으로 추상 클래스처럼 동작한다.
- enum 상수별로 오버라이딩을 해서 enum을 객체 지향적인 방식으로 활용할 때 유용하다.
public enum Fruit { APPLE { @Override public String feature() { return "round and red"; } }, BANANA { @Override public String feature() { return "long and yellow"; } }, ORANGE { @Override public String feature() { return "round and orange"; } }, MANGO { @Override public String feature() { return "circle and yellow"; } } // 추상 메소드 정의 public abstract String feature(); }
enum을 가지고 Set 자료구조를 만들 수 있도록 고안된 Set 인터페이스 구현체이다.
java.lang.Object ↳java.util.AbstractCollection<E> ↳ java.util.AbstractSet<E> ↳ java.util.EnumSet<E>
- EnumSet은 AbstractSet 클래스를 상속하고 Set 인터페이스를 구현한다.
- HashSet보다 빠르다.
- enum 상수만 값으로 가질 수 있으며, 모든 값은 같은 enum 타입이어야 한다.
- null 객체를 허용하지 않으며, 허용할 경우 NullPointerException이 발생한다.
- EnumSet의 내부는 추상 클래스이기 때문에 객체로서 생성 및 사용이 불가능하다. (즉, new 연산자 사용 불가)