728x90
📍 학습 목표
열거형(enum)은 자바5에서 추가된 자료형으로 '의미가 부여된 이름'을 갖는 '상수'의 선언에 그 목적이 있다.
열거형(enum)가 추가 되기 전/후를 비교해서 어떤 점이 달라졌고 열거형이 무엇인지 알아보자!
열거형(enum) 추가 되기 전
인터페이스 기반의 상수 정의
interface Scale {
int DO = 0; int RE = 1; int MI = 2; int FA = 3;
int SO = 4; int RA = 5; int SI = 6;
}
- 인터페이스 내에 선언된 변수는 public, static, final이 선언된 것으로 간주한다.
🔍 인터페이스 기반의 상수 정의의 문제점
public class Prac {
public static void main (String[] args) {
who(Person.MAN);
who(Animal.DOG);
}
public static void who(int man) {
switch(man) {
case Person.MAN:
System.out.println("남성 손닙입니다.");
break;
case Person.WOMAN:
System.out.println("여성 손님입니다.");
break;
} // end of switch
}
}
interface Animal {
int DOG = 1;
int CAT = 2;
}
interface Person {
int MAN = 1;
int WOMAN = 2;
}
남성 손닙입니다.
남성 손닙입니다.
- Person.MAN의 값이 1이다. 하지만 Animal.DOG의 값도 1이다. 그렇기 때문에 who() 메서드에 어떤 인터페이스의 상수가 오든 int 타입일 경우 컴파일 오류와 실행 오류를 발생시키지 않는다.
- 이런 문제를 해결하기 위해서 자바5에서 열거형(enum)이 추가됐다.
public class Prac {
public static void main (String[] args) {
System.out.println(Animal.CAT == Person.WOMAN);
}
}
interface Animal {
int DOG = 1;
int CAT = 2;
}
interface Person {
int MAN = 1;
int WOMAN = 2;
}
true
- 인터페이스 기반의 상수를 정의의 문제점을 더 자세히 보자면, Animal.CAT 과 Person.WOMAN은 서로 값은 같지만 타입이 달라서 false 값이 나와야 하지만 인터페이스 기반의 상수 정의에서는 true가 나온다.
열거형(enum) 추가
열거형 (enum)
- 모든 열거형은 java.lang.Enum 클래스의 자손이다.
- 열거형은 서로 관련된 상수를 편리하게 선언하기 위한 것으로 여러 상수를 정의할 때 사용하면 유용하다.
- 열거형은 '타입에 안전한 열거형(typesafe enum)'이라서 실제 값이 같아도 타입이 다르면 컴파일 에러가 발생한다.
- 인터페이스 기반의 상수 정의의 문제점 해결!
- enum 타입은 고정된 상수들의 집합으로 런타임이 아닌 컴파일 타임에 모든 값을 알고 있어야한다.
- 상수의 값이 바뀌면, 해당 상수를 참조하는 모든 소스를 다시 컴파일 해야한다. 하지만 열거형 상수를 사용하면, 기존의 소스를 다시 컴파일 하지 않아도 된다.
- 열거형 상수간의 비교에는 ==, equals() 를 사용할 수 있다. 그러나 ==의 속도가 더 빠르다.
- ==는 객체의 주소 비교이다. 열거형의 상수는 값이 바뀌지 않으므로 ==로 비교 가능한 것이다.
- <, > 같은 비교 연산자는 사용할 수 없으나 compareTo()는 사용 가능하다.
- compareTo()로 비교할 때, 두 열거형 상수의 ordinal() 값을 서로 뺴준다. 비교 대상이 같으면 0. 왼쪽이 크면 양수, 오른쪽이 크면 음수를 반환한다.
- Enum 클래스는 Comparable<E>를 구현한 클래스다.
- 열거형의 생성자는 private만 선언할 수 있으나 생략 가능하다.
- 열거형의 생성자는 외부에서 호출불가
🔍 열거형의 정의
💡 방법1
enum 열거형이름 {상수명A, 상수명B, ...}
💡 방법2
enum 열거형이름 {상수명A(1), 상수명B(2), ...}
- 열거형 상수의 값이 불연속적인 경우에는 열거형 상수의 이름 옆에 원하는 값을 괄호()와 함께 적어주면 된다.
- 지정된 값을 저장할 수 있는 인스턴스 변수와 생성자를 새로 추가해줘야 한다.
- 이 때 열거형 상수를 먼저 정의한 다음에 열거형 상수 마지막에 ';' 를 붙인 후에 다른 멤버들을 추가해야한다.
- 하나의 열거형 상수에 여러 값을 지정할 수도 있다. 다만 그에 맞게 인스턴스 변수와 생성자 등을 새로 추가해야 한다.
더보기
더보기
예제1
enum Direction {
EAST(1), SOUTH(5), WEST(-1), NORTH(10); // 끝에 ; 추가할 것
private int value;
Direction(int value) {
this.value = value;
}
}
예제2
enum Direction {
EAST(1, ">"), SOUTH(5, "V"), WEST(-1, "<"), NORTH(10, "^");
private int value;
private String symbol;
Direction(int value, String symbol) {
this.value = value;
this.symbol = symbol;
}
}
🔍 Enum 클래스의 메서드
메서드 | 설명 |
Class<E> getDeclaringClass() | 열거형의 Class객체를 반환 |
String name() | 열거형 상수의 이름을 문자열로 반환 |
int ordinal() | 열거형 상수가 정의된 순서를 반환 (0부터 시작) |
static <T extends Enum<T>> valueOf(Class<T>, enumType, String name) | 지정된 열거형에서 name과 일치하는 열거형 상수를 반환 |
int compareTo(E o) | 비교한다. |
- ordinal()이 열거형 상수에 정의된 순서를 반환하지만, 이 값을 열거형 상수의 값으로 사용하지 않는 것이 좋다. 이 값은 내부적인 용도로만 사용되기 위한 것이기 때문이다.
💡 컴파일러가 자동으로 추가해주는 메서드
메서드 | 설명 |
static E[] values() | 열거형의 모든 상수를 배열에 담아 반환 |
static E valueOf(String name) | 열거형 상수의 이름으로 문자열 상수에 대한 참조를 얻을 수 있다. |
더보기
더보기
public class Prac {
public static void main (String[] args) {
Direction d = Direction.valueOf("WEST");
System.out.println(d);
System.out.println(Direction.WEST == Direction.valueOf("WEST"));
}
}
enum Direction {
EAST, SOUTH, WEST, NORTH
}
WEST
true
🔍 예제
예제1
public class Prac {
public static void main (String[] args) {
Direction d1 = Direction.EAST;
Direction d2 = Direction.valueOf("WEST");
Direction d3 = Enum.valueOf(Direction.class, "EAST");
System.out.println("d1 = " + d1);
System.out.println("d2 = " + d2);
System.out.println("d3 = " + d3);
System.out.println();
System.out.println("d1 == d2 ? " + (d1==d2));
System.out.println("d1 == d3 ? " + (d1==d3));
System.out.println("d1.equals(d3) ? " + d1.equals(d3));
System.out.println("d1.compareTo(d3) ? " + (d1.compareTo(d3)));
System.out.println("d1.compareTo(d2) ? " + (d1.compareTo(d2)));
System.out.println();
move(d1);
System.out.println();
Direction[] dArr = Direction.values();
for(Direction d: dArr)
System.out.printf("%s = %d%n", d.name(), d.ordinal());
}
static void move(Direction dir) {
switch(dir) {
case EAST:
System.out.println("EAST"); // Direction.EAST라고 쓰면 안된다.
break;
case SOUTH:
System.out.println("SOUTH");
break;
case WEST:
System.out.println("WEST");
break;
case NORTH:
System.out.println("NORTH");
break;
default:
System.out.println("Invalid diraction");
} // end of switch
}
}
enum Direction {
EAST, SOUTH, WEST, NORTH
}
d1 = EAST
d2 = WEST
d3 = EAST
d1 == d2 ? false
d1 == d3 ? true
d1.equals(d3) ? true
d1.compareTo(d3) ? 0
d1.compareTo(d2) ? -2
EAST
EAST = 0
SOUTH = 1
WEST = 2
NORTH = 3
- swtich문의 조건식에도 열거형을 사용할 수 있다.
- 주의할 점은 case문에 열거형의 이름은 적지 않고 상수의 이름만 적어줘야한다.
예제2
public class Prac {
public static void main (String[] args) {
for(Direction d : Direction.values())
System.out.printf("%s = %d%n", d.name(), d.getValue());
Direction d1 = Direction.EAST;
Direction d2 = Direction.of(1);
System.out.println();
System.out.printf("d1 = %s, %d%n", d1.name(), d1.getValue());
System.out.printf("d2 = %s, %d%n", d2.name(), d2.getValue());
System.out.println(Direction.EAST.rotate(1));
System.out.println(Direction.EAST.rotate(2));
System.out.println(Direction.EAST.rotate(-1));
System.out.println(Direction.EAST.rotate(-2));
}
}
enum Direction {
EAST(1, ">"), SOUTH(5, "V"), WEST(-1, "<"), NORTH(10, "^");
private static final Direction[] DIR_ARR = Direction.values();
private int value;
private String symbol;
Direction(int value, String symbol) {
this.value = value;
this.symbol = symbol;
}
public int getValue() {return value;}
public String getSymbol() {return symbol;}
public static Direction of(int dir) {
// 0~3 범위 벗어나면 예외 발생
if(dir < 1 || dir > 4)
throw new IllegalArgumentException("Invalid value : " + dir);
return DIR_ARR[dir-1];
}
// 방향을 회전시키는 메서드. num의 값만큼 90도씩 시계방향으로 회전한다.
public Direction rotate(int num) {
num = num % 4;
if(num<0) num+=4; // num이 음수일 때는 시계 반대 방향으로 회전
return DIR_ARR[(value-1+num) %4];
}
public String toString() {
return name() + getSymbol();
}
}
EAST = 1
SOUTH = 5
WEST = -1
NORTH = 10
d1 = EAST, 1
d2 = EAST, 1
SOUTHV
WEST<
NORTH^
WEST<
예제3 ㅡ 열거형에 추상 메서드 추가하기
public class Prac {
public static void main (String[] args) {
System.out.println("bus fare = " + Transportation.BUS.fare(100));
System.out.println("train fare = " + Transportation.TRAIN.fare(100));
System.out.println("ship fare = " + Transportation.SHIP.fare(100));
System.out.println("airplane fare = " + Transportation.AIRPLANE.fare(100));
}
}
enum Transportation {
BUS(100) {
int fare(int distance) {
return distance * BASIC_FARE;
}
},
TRAIN(150){
int fare(int distance) {
return distance * BASIC_FARE+ 50;
}
},
SHIP(100){
int fare(int distance) {
return distance * BASIC_FARE + 100;
}
},
AIRPLANE(300){int fare(int distance) {return distance * BASIC_FARE * 2;}};
protected int BASIC_FARE; // protected로 해야 각 상수에서 접근 가능
Transportation(int basicFare) {
BASIC_FARE = basicFare;
}
public int getBasicFare() {return BASIC_FARE;}
abstract int fare(int distance);
}
bus fare = 10000
train fare = 15050
ship fare = 10100
airplane fare = 60000
- 열거형에 추상 메서드 fare(int distance)를 선언하면 각 열거형 상수가 이 주상 메서드를 반드시 구현해야 한다.
- 각 열거형 상수가 추상 메서드 fare()를 각기 다른 내용으로 구현할 수 있어서 거리에 따라 요금을 계산하는 방식을 운송 수단마다 다르게 할 수 있다.
728x90
'[자바] > 자바' 카테고리의 다른 글
[Java] Optional<T> 클래스 (0) | 2022.02.07 |
---|---|
[Java] 메서드 참조 (Method References) (0) | 2022.02.04 |
[Java] 함수형 인터페이스 (0) | 2022.02.03 |
[Java] 내부 클래스, 네스티드(Nested) 클래스, 익명 클래스 (0) | 2022.01.27 |
[Java] 애노테이션 (annotation) (0) | 2022.01.26 |