https://github.com/whiteship/live-study/issues/11
목표
자바의 열거형에 대해 학습하세요.
학습할 것 (필수)
- enum 정의하는 방법
- enum이 제공하는 메소드 ( values()와 valueOf() )
- java.lang.Enum
- EnumSet
enum 정의하는 방법
public enum Season {
SPRING(3, 4, 5),
SUMMER(6, 7, 8),
AUTUMN(9, 10, 11),
WINTER(12, 1, 2);
Season(int i, int i1, int i2) {
this.start = i;
this.middle = i1;
this.end = i2;
}
private final int start;
private final int middle;
private final int end;
public int getStart() {
return start;
}
public int getMiddle() {
return middle;
}
public int getEnd() {
return end;
}
}
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class MySeasonTest {
Season season;
public MySeasonTest(Season season) {
this.season = season;
}
public void showMeTheSeason() {
System.out.println(season.getStart());
System.out.println(season.getMiddle());
System.out.println(season.getEnd());
}
@Test
@DisplayName("main")
public static void main(String[] args) {
MySeasonTest mySeasonTest
= new MySeasonTest(Season.WINTER);
mySeasonTest.showMeTheSeason();
}
}
> Task :MySeasonTest.main()
12
1
2
BUILD SUCCESSFUL in 415ms
3 actionable tasks: 2 executed, 1 up-to-date
- Java의 enum 타입은 클래스
- 상수이기에 변수명은 대문자
- 암시적으로 java.lang.Enum을 상속(extends)
- enum 생성자는 반드시 package-private, private 접근
- enum 시작 부분에 정의된 상수를 자동으로 생성
- enum 생성자를 직접 호출 불가능
- 암시적으로 public static final
출처, 참고:
https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html
https://docs.oracle.com/javase/specs/jls/se13/html/jls-8.html#jls-8.9.2
https://woowabros.github.io/tools/2017/07/10/java-enum-uses.html
enum이 제공하는 메소드 ( values()와 valueOf() )
@Test
@DisplayName("main")
public static void main(String[] args) {
MySeasonTest mySeasonTest
= new MySeasonTest(Season.WINTER);
// valueOf()
Season tempSeason
= mySeasonTest.season.valueOf("SUMMER");
System.out.println("tempSeason.getStart(): "
+ tempSeason.getStart());
// values()
for (Season s :
Season.values()) {
System.out.println(s + ", "
+ s.getStart() + ", "
+ s.getMiddle() + ", "
+ s.getEnd());
}
// ordinal()
for (Season s :
Season.values()) {
System.out.println(
s + " at index " + s.ordinal());
}
}
> Task :MySeasonTest.main()
// valueOf()
tempSeason.getStart(): 6
// values()
SPRING, 3, 4, 5
SUMMER, 6, 7, 8
AUTUMN, 9, 10, 11
WINTER, 12, 1, 2
// ordinal()
SPRING at index 0
SUMMER at index 1
AUTUMN at index 2
WINTER at index 3
BUILD SUCCESSFUL in 411ms
3 actionable tasks: 2 executed, 1 up-to-date
- valueOf(): 지정한 이름의 enum 상수를 반환
- values(): 해당 enum의 모든 상수를 저장한 배열을 생성하여 반환
- ordinal(): enum 상수의 순서값 반환
- toString(): enum 상수의 이름을 반환
- name(): enum 상수의 이름을 반환
toString() 메소드가 사용자에게 더 친숙한 이름을 반환하므로 name() 메소드보다 우선적으로 사용해야 합니다. (Most programmers should use the toString() method in preference to this one, as the toString method may return a more user-friendly name.)
출처: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Enum.html#name()
Enum 바이트코드(ByteCode)
public enum Day {
SUNDAY, MONDAY, TUESDAY
}
javap -c Day.class
Compiled from "Day.java"
public final class com.testcompany.Day extends java.lang.Enum<com.testcompany.Day> {
public static final com.testcompany.Day SUNDAY;
public static final com.testcompany.Day MONDAY;
public static final com.testcompany.Day TUESDAY;
public static com.testcompany.Day[] values();
Code:
0: getstatic #13 // Field $VALUES:[Lcom/testcompany/Day;
3: invokevirtual #17 // Method "[Lcom/testcompany/Day;".clone:()Ljava/lang/Object;
6: checkcast #18 // class "[Lcom/testcompany/Day;"
9: areturn
public static com.testcompany.Day valueOf(java.lang.String);
Code:
0: ldc #1 // class com/testcompany/Day
2: aload_0
3: invokestatic #22 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #1 // class com/testcompany/Day
9: areturn
static {};
Code:
0: new #1 // class com/testcompany/Day
3: dup
4: ldc #32 // String SUNDAY
6: iconst_0
7: invokespecial #33 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #3 // Field SUNDAY:Lcom/testcompany/Day;
13: new #1 // class com/testcompany/Day
16: dup
17: ldc #34 // String MONDAY
19: iconst_1
20: invokespecial #33 // Method "<init>":(Ljava/lang/String;I)V
23: putstatic #7 // Field MONDAY:Lcom/testcompany/Day;
26: new #1 // class com/testcompany/Day
29: dup
30: ldc #35 // String TUESDAY
32: iconst_2
33: invokespecial #33 // Method "<init>":(Ljava/lang/String;I)V
36: putstatic #10 // Field TUESDAY:Lcom/testcompany/Day;
39: invokestatic #36 // Method $values:()[Lcom/testcompany/Day;
42: putstatic #13 // Field $VALUES:[Lcom/testcompany/Day;
45: return
}
- 놀랍게도 바이트코드 내부에 'public static final' 선언
- values(), valueOf()도 미리 준비
- 메소드의 형태로 static 필드에 선언
바이트 코드 설명
- getstatic: 클래스의 static 필드값 가져오기
- invokevirtual: 인스턴스 메서드 호출
- checkcast: 객체의 타입 확인
- areturn: 메소드 참조 반환 (Return reference from method)
- ldc: 런타임 상수 풀에 아이템 푸시
- aload_<n>: 지역변수 레퍼런스 로드
- invokestatic: static 메서드 호출
- new: 새로운 객체 생성
- dup: 연산자 스택의 맨 위의 값 복사
- iconst_<i>: int 상수 푸시
- invokespecial: 생성자, private 메소드, 슈퍼 클래스의 메소드 호출
- putstatic: static 필드 선언
- return: Return void from method
여기서 관심을 가질 부분은 "Ljava/lang/String;"과 마지막의 "V"이다. 자바 바이트코드의 표현에서 "L;"은 클래스 인스턴스이다. 즉, addUser() 메서드는 java/lang/String 객체 하나를 파라미터로 받는 메서드이다. 이 사례의 라이브러리에서는 파라미터가 변경되지 않았으므로 이 파라미터는 정상이다. 위 메시지 마지막의 "V"는 메서드의 반환값을 나타낸다. 자바 바이트코드 표현에서 "V"는 반환값이 없음을 의미한다.
참고, 출처:
https://d2.naver.com/helloworld/1230
https://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html
java.lang.Enum
public abstract class Enum<E extends Enum<E>>
implements Constable, Comparable<E>, Serializable {
private final String name;
@NotNull
public final String name() {
return name;
}
private final int ordinal;
@Range(from = 0, to = java.lang.Integer.MAX_VALUE)
public final int ordinal() {
return ordinal;
}
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public String toString() {
return name;
}
...
}
- 모든 자바 열거형의 기본 클래스
- Enum은 추상 클래스. Enum 클래스를 생성할 수 없다.
- final 메소드로 선언되므로 수정이 불가능
EnumSet
// Creates an enum set containing all of the elements
// in the specified element type.
// 명시한 요소 타입이 모두 들어간 enum 세트 생성
EnumSet<Season> myEnumSet = EnumSet.allOf(Season.class);
System.out.println("allOf(Season.class): " + myEnumSet);
// Creates an empty enum set with the specified element type.
// 명시한 요소 타입을 사용해 빈 enum 세트 생성
myEnumSet = EnumSet.noneOf(Season.class);
System.out.println("noneOf(Season.class): " + myEnumSet);
// Creates an enum set initially containing the specified element.
// 생성시 명시한 요소가 들어간 enum 세트 생성
myEnumSet = EnumSet.of(Season.SPRING, Season.AUTUMN);
System.out.println("of(Season.SPRING, Season.AUTUMN): " + myEnumSet);
// Creates an enum set with the same element type
// as the specified enum set, initially containing
// all the elements of this type that are not contained
// in the specified set.
// 명시한 세트가 포함되지 않은 enum 세트 생성
myEnumSet = EnumSet.complementOf(myEnumSet);
System.out.println("complementOf(myEnumSet): " + myEnumSet);
// Creates an enum set initially containing all of the elements
// in the range defined by the two specified endpoints.
// 지정된 두 요소 범위안의 enum 세트를 생성
myEnumSet = EnumSet.range(Season.SUMMER, Season.AUTUMN);
System.out.println("range(Season.SPRING, Season.AUTUMN): " + myEnumSet);
allOf(Season.class): [SPRING, SUMMER, AUTUMN, WINTER]
noneOf(Season.class): []
of(Season.SPRING, Season.AUTUMN): [SPRING, AUTUMN]
complementOf(myEnumSet): [SUMMER, WINTER]
range(Season.SPRING, Season.AUTUMN): [SUMMER, AUTUMN]
Process finished with exit code 0
- enum 타입과 함께 사용하기 위한 특수 Set의 구현
- 모든 요소는 enum 셋을 만들 때 명시적으로 또는 암시적으로 지정된 단일 enum 타입을 가져와야 함
- 내부적으로 bit vector로 표시됨 (매우 간결하고 효율적)
- 이 클래스의 공간 및 시간 성능은 기존의 int 기반 "비트 플래그"의 대안으로 고품질에 타입 안전으로 사용할 수 있을 만큼 충분히 우수해야 함
출처: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/EnumSet.html
댓글 없음:
댓글 쓰기