-
Java 항해일지 11. Enum공부일기/자바 스터디 2021. 1. 26. 15:42
목표
자바의 열거형에 대해 학습하세요.
학습할 것 (필수)
- enum 정의하는 방법
- enum이 제공하는 메소드 (values()와 valueOf())
- java.lang.Enum
- EnumSet
enum이란?
enum은 열거형(enumerated type)이라고 부른다. 열거형은 서로 연관된 상수들의 집합이라고 할 수 있다.
열거형은 연관된 값들을 저장하며, 저장된 값들이 변경되지 않도록 보장한다. 또한 enum의 경우 열거형 그 자체이면서 클래스이기 때문에 enum내부에 생성자나 필드, 메서드를 가질 수 있어 다양한 역할을 할 수 있다.
상수 목록이 필요해 상수목록만 적어놓은 class를 활용한 적이 있는데, 이런 사용은 자제하고 enum을 활용할 수 있다.
enum 정의하는 방법
1. 별도의 java 파일로 정의
2. 클래스 내부에서 정의
public class MonthClass { public enum Month { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC; } public static void main(String[] args) { Month jan = Month.JAN; System.out.println(jan); } }
3. 클래스 외부에서 정의
enum Month { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC; } public class MonthClass { public static void main(String[] args) { Month jan = Month.JAN; System.out.println(jan); } }
몇몇 블로그에서 ;(세미콜론)은 생략할 수 없고 반드시 사용해야 한다는 것을 봤는데 이는 잘못된 사실이다. 상수로 사용할 목록만 정의하는 경우 세미콜론은 생략 가능하고 실제로 인텔리제이에서 세미콜론을 붙여봤는데 아래와 같이 불필요한 세미콜론을 제거할 것을 추천했다.
enum이 유용하게 사용될 수 있는 상황은 어떤 상황이 있을까?
예를 들어 숫자만 알고, 영어는 아예 모르는 사람이 '26, JAN'과 같이 표기된 달력을 본다고 가정해보자. 이 사람은 26이 날짜인 건 알고 JAN은 모른다. 이때, JAN을 입력했을 때 숫자로 바꿔서 알려주는 프로그램의 코드는 아래와 같을 것이다. (예제는 Enum을 별도의 파일로 정의했다.)
public class MonthClass { public static void main(String[] args) { // JAN 입력 받음 MonthEnum month = MonthEnum.JAN; int numOfMonth = 0; switch (month) { case JAN: numOfMonth = 1; break; case FEB: numOfMonth = 2; break; case MAR: numOfMonth = 3; break; case APR: numOfMonth = 4; break; case MAY: numOfMonth = 5; break; case JUN: numOfMonth = 6; break; case JUL: numOfMonth = 7; break; case AUG: numOfMonth = 8; break; case SEP: numOfMonth = 9; break; case OCT: numOfMonth = 10; break; case NOV: numOfMonth = 11; break; case DEC: numOfMonth = 12; break; } System.out.println(month + ": " + numOfMonth + "월 입니다."); } }
enum 작성에 switch문까지 작성할 코드가 굉장히 많다. enum을 제대로 활용하고 있는 코드라고 할 수는 없다.
enum의 각 상수를 대표하는 정수를 지정해서 사용한다면? 코드는 다음과 같이 간결해 질 수 있다.
먼저 enum을 아래와 같이 수정한다.
public enum MonthEnum { JAN(1), FEB(2), MAR(3), APR(4), MAY(5), JUN(6), JUL(7), AUG(8), SEP(9), OCT(10), NOV(11), DEC(12); private int numOfMonth; MonthEnum(int numOfMonth) { this.numOfMonth = numOfMonth; } public int getNumOfMonth() { return numOfMonth; } }
각 상수를 대표하는 정수 값을 지정해주고, 생성자를 만들고, 정수 값을 반환받는 메서드도 만들어줬다.
enum이 수정됐다면 메인메서드는 아래와 같이 간결해질 수 있다.
public class MonthClass { public static void main(String[] args) { // JAN 입력 받음 MonthEnum month = MonthEnum.JAN; System.out.println(month + ": " + MonthEnum.JAN.getNumOfMonth() + "월 입니다."); } }
훨씬 간결한 코드가 같은 값을 출력해주고 있다.
이번에는 숫자를 모르고 영어밖에 모르는 사람이 있다고 가정해보자. 이 사람은 JAN과 같이 축약된 단어는 알아볼 수가 없고, January같이 full name을 이용해서만 달력을 읽을 수 있다. 이런 사람을 위한 코드는 아래와 같다.
public enum MonthEnum { JAN(1, "January"), FEB(2, "February"), MAR(3, "March"), APR(4, "April"), MAY(5, "May"), JUN(6, "June"), JUL(7, "July"), AUG(8, "August"), SEP(9, "September"), OCT(10, "October"), NOV(11, "November"), DEC(12, "December"); private int numOfMonth; private String fullNameOfMonth; MonthEnum(int numOfMonth) { this.numOfMonth = numOfMonth; } MonthEnum(int numOfMonth, String fullNameOfMonth) { this.numOfMonth = numOfMonth; this.fullNameOfMonth = fullNameOfMonth; } public int getNumOfMonth() { return numOfMonth; } public String getFullNameOfMonth() { return fullNameOfMonth; } }
생성자 오버라이딩을 통한 enum을 수정하고 메인메서드를 다음과 같이 수정했다.
public class MonthClass { public static void main(String[] args) { // JAN 입력 받음 MonthEnum month = MonthEnum.JAN; System.out.println(month + ": '" + MonthEnum.JAN.getFullNameOfMonth() + "' 입니다."); } }
만약 숫자와 full name을 같이 보고 싶다면 메인메서드를 다음과 같이 수정할 수 있을 것이다.
public class MonthClass { public static void main(String[] args) { // JAN 입력 받음 MonthEnum month = MonthEnum.JAN; System.out.println(month + ": " + MonthEnum.JAN.getNumOfMonth() + "월, full-name은 '" + MonthEnum.JAN.getFullNameOfMonth() + "' 입니다."); } }
이러한 기능 뿐만 아니라 함수형 인터페이스를 사용할 수도 있다. 아래는 각 달의 주제를 함수형 인터페이스를 통해 출력한 예제이다.
"January", "February" 등 일반 문자열을 출력하는 것과 같은 기능이지만, 어떻게 구현했는지가 다르다.
import java.util.function.Supplier; public enum MonthEnum { JAN(1, "January", () -> "주제가 있는 달"), FEB(2, "February", () -> "생각의 달"), MAR(3, "March", () -> "생명의 달"), APR(4, "April", () -> "과학의 달"), MAY(5, "May", () -> "가정의 달"), JUN(6, "June", () -> "호국 보훈의 달"), JUL(7, "July", () -> "자연과 환경의 달"), AUG(8, "August", () -> "환상의 달"), SEP(9, "September", () -> "독서의 달"), OCT(10, "October", () -> "문화의 달"), NOV(11, "November", () -> "불조심 강조의 달"), DEC(12, "December", () -> "나눔 달"); private int numOfMonth; private String fullNameOfMonth; private Supplier<String> monthInfo; MonthEnum(int numOfMonth, String fullNameOfMonth, Supplier<String> monthInfo) { this.numOfMonth = numOfMonth; this.fullNameOfMonth = fullNameOfMonth; this.monthInfo = monthInfo; } public void printMonthInfo() { System.out.println(monthInfo.get()); } }
public class MonthClass { public static void main(String[] args) { // JAN 입력 받음 MonthEnum month = MonthEnum.JAN; month.printMonthInfo(); } }
enum이 제공하는 메소드 (values()와 valueOf())
enum이 제공하는 기본적인 메서드들은 values()와 valueOf() 외에도 ordinal(), name() 등이 있다. 예제를 통해 살펴보자.
values()
enum 안에 있는 모든 상수들을 배열로 반환한다.
import java.util.Arrays; public class MonthClass { public static void main(String[] args) { Arrays.stream(MonthEnum.values()).forEach(System.out::println); } }
import java.util.Arrays; public class MonthClass { public static void main(String[] args) { Arrays.stream(MonthEnum.values()).forEach(m -> m.printMonthInfo()); } }
valueOf()
String타입을 매개변수로 받아 해당 매개변수와 동일한 이름의 상수를 찾아 반환한다. 없는 경우 IllegalArgumentException을 발생시킨다.
public class MonthClass { public static void main(String[] args) { System.out.println(MonthEnum.valueOf("Jan")); //JAN인 경우 JAN, Jan은 아래와 같이 예외 출력 } }
java.lang.Enum
enum 클래스는 java.lang.Enum 클래스를 상속받았으므로 다른 클래스를 상속받을 수 없다.(다중 상속 불가)
아래는 Enum 클래스에서 사용할 수 있는 다양한 메서드들의 예시다.
ordinal()
해당 상수가 Enum 클래스 내에 몇 번째로 선언됐는지를 반환한다.
public class MonthClass { public static void main(String[] args) { MonthEnum month = MonthEnum.JAN; System.out.println(month.ordinal()); } }
name()
상수의 값을 반환한다.
public class MonthClass { public static void main(String[] args) { MonthEnum month = MonthEnum.JAN; System.out.println(month.name()); } }
compareTo(E o)
비교하고자 하는 상수와 위치 값의 차이를 반환한다. (AUG = 7, JAN = 0)
public class MonthClass { public static void main(String[] args) { MonthEnum month = MonthEnum.AUG; System.out.println(month.compareTo(MonthEnum.JAN)); } }
EnumSet
EnumSet 클래스는 java.util 패키지에 정의되어 있으며, 우리가 흔히 알고 있는 Set 자료구조의 특징을 따른다.
import java.util.EnumSet; public class MonthClass { public static void main(String[] args) { EnumSet<MonthEnum> enumSet = EnumSet.allOf(MonthEnum.class); // 모든 요소를 포함한 EnumSet 반환 EnumSet<MonthEnum> winterEnumSet = EnumSet.of(MonthEnum.DEC, MonthEnum.JAN, MonthEnum.FEB); // 특정 상수만 포함한 EnumSet 반환 EnumSet<MonthEnum> exceptWinterSet = enumSet.complementOf(winterEnumSet); // 특정 상수를 제외한 EnumSet 반환 EnumSet<MonthEnum> springEnumSet = EnumSet.range(MonthEnum.MAR, MonthEnum.MAY); // 특정 범위의 EnumSet 반환, MAR, APR, MAY System.out.println(enumSet); System.out.println(winterEnumSet); System.out.println(exceptWinterSet); System.out.println(springEnumSet); } }
참고.
docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Enum.html
'공부일기 > 자바 스터디' 카테고리의 다른 글
Java 항해일지 - 14. 제네릭 (0) 2021.03.01 Java 항해일지 - 12. 애노테이션 (0) 2021.02.02 Java 항해일지 10. 멀티스레드 프로그래밍 (0) 2021.01.24 Java 항해일지 - 9. 예외 처리 (0) 2021.01.17 Java 항해일지 - 8. 인터페이스 (0) 2021.01.08