-
반응형
Enum (JDK 1.5 ~)
자바의 열거형으로, 열거형이 갖는 값 뿐 아니라 타입까지 관리하기 때문에 보다 논리적인 오류를 줄일 수 있음
= 타입에 안전한 열거형(typesafe enum)
예제
class Card { static final int CLOVER = 0; static final int HEART = 1; static final int DIAMOND = 2; static final int SPADE = 3; static final int TWO = 0; static final int THREE = 1; static final int FOUR = 2; final int kind; final int num; } class Card { enum Kind { CLOVER, HEART, DIAMOND, SPADE } // 열거형 Kind를 정의 enum VALUE { TWO, THREE, FOUR } // 열거형 Value를 정의 final Kind kind; // 타입이 int가 아닌 Kind final Value value; // 타입이 int가 아닌 Value }
타입 안전한 열거형(Typesafe Enum)
실제 값이 같아도 타입이 다르면 조건식의 결과가 false
값뿐만 아니라 타입까지 체크하기 때문에 안전하다고 하는 것
if(Card.CLOVER == Card.TWO) // true지만 false여야 의미가 맞음 // 타입 형태가 다르므로 가능한 부분 if(Card.Kind.CLOVER == Card.Value.TWO) // false. 값은 같지만 타입이 다름
상수의 경우 값이 바뀌면, 해당 상수를 참조하는 모든 소스를 다시 컴파일해야 함
열거형 상수를 사용하면, 기존의 소스를 다시 컴파일하지 않아도 됨
열거형의 정의와 사용
정의하는 방법
괄호{} 안에 상수의 이름을 나열하면 됨
enum 열거형이름 { 상수명1, 상수명2, ... }
예제
enum Area { ONE, TWO, THREE, FOUR }
사용하는 방법
열거형에 정의된 상수를 사용하는 방법 = enum명.상수명 (=클래스의 static변수 참조와 동일)
class Graph { int x, y; // 그래프 위치 Area area; // 열거형을 인스턴스 변수로 선언 void init() { area = Area.ONE; // 그래프의 구역을 ONE으로 초기화 } }
열거형 상수간의 비교엔 '=='를 사용
equlas()가 아닌 '=='로 비교가 가능하다는 것은 그만큼 빠른 성능을 제공
'<', '>' 같은 비교연산자는 사용 불가, compareTo() 사용가능
compareTo() : 두 비교대상이 같으면 0, 왼쪽이 크면 양수, 오른쪽이 크면 음수
if (area == AREA.ONE) { x++; } else if (area > AREA.ONE) { // Error. 열거형 상수에 비교연산자 사용 불가 ... } else if (area.compareTo(AREA.ONE) > 0) { // compareTo()는 가능 ... }
switch의 조건식에도 열거형 사용 가능 (단, case문엔 열거형명은 적지 않고 상수명만 적어야 한다는 제약이 있음)
void move() { switch(area) { case ONE: x++; // Area.ONE이라고 쓰면 안 됨 break; case TWO: x--; break; case THREE: y++; break; case FOUR: y--; break } }
모든 열거형의 조상 - java.lang.Enum
컴파일러가 자동으로 추가해주는, 모든 열거형이 가지고 있는 것
위의 예제였던 열거형 Area에 정의된 모든 상수를 출력하려면?
Area[] aArr = Area.values(); for(Area a : aArr) // for(Area a : Area.values()) System.out.printf("%s=%d%n", a.name(), a.ordinal());
values() = 열거형의 모든 상수를 배열에 담아 반환함
// 열거형의 Class객체 반환 Class<E> getDeclaringClass() // 열거형 상수의 이름을 문자열로 반환 String name() // 열거형 상수가 정의된 순서 반환 (0부터 시작) int ordinal() // 지정된 열거형에서 name과 일치하는 열거형 상수 반환 T valueOf(Class<T> enumType,, String name) // 열거형의 모든 상수를 배열에 담아 반환 static E values() // 열거형 상수의 이름으로 문자열 상수에 대한 참조를 얻을 수 있게 해줌 static E valueOf(String name)
열거형에 멤버 추가
Enum클래스에 정의된 ordinal()은 열거형 상수가 정의된 순서를 반환
하지만 열거형 상수의 값으로 사용하지 않는 것이 좋음(= 내부적인 용도로 사용되는 값이기 때문)
열거형 상수의 값이 불연속적인 경우엔, 열거형 상수의 이름 옆에 원하는 값을 괄호()와 함께 적어주면 됨
enum Area { ONE(1), TWO(4), THREE(-3), FOUR(10) }
지정할 값을 저장할 수 있는 인스턴스 변수 및 생성자를 새로 추가해주어야 함
주의) 열거형 상수를 먼저 정의한 후 다른 멤버들을 추가해야 함, 또한 열거형 상수의 마지막에 ';'도 잊지 말아야 함
enum Area { ONE(1), TWO(4), THREE(-3), FOUR(10); // 꼭 ;를 추가해야 함 /* 열거형의 인스턴스 변수는 반드시 final일 필요는 없음 * 하지만 value는 열거형 상수의 값을 저장하기 위한 것이므로 final + getValue()추가 */ private final int value; // 정수를 저장한 필드(인스턴스 변수)를 추가 Area(int value) { this.value = value; } // 생성자 추가 public int getValue() { return value; } }
열거형의 생성자는 외부에선 호출이 불가능함. (= 묵시적으로 private이기 때문)
필요하다면 하나의 열거형 상수에 여러 값을 지정할 수도 있음. 단) 인스턴스 변수 및 생성자 등을 새로 추가해주어야 함
enum Area { ONE(1, "<<"), TWO(2, ">>"), THREE(3, "<>"), FOUR(4, "><"); private final int value; private final String symbol; AREA(int value, String symbol) { // private 접근 제어자가 생략됨 this.value = value; this.symbol = symbol; } public int getValue() { return value; } public String getString() { return symbol; } }
열거형에 추상 메서드 추가하기
열거형 CalcOTTF는 연산자의 종류별로 열거형 상수를 정의하고 있으며, 각 연산자에는 피연산자(operand)가 적혀져있음
enum CalcOTTF { PLUS(1), MINUS(2), MULTIPLY(3), DIVIDE(4); private final int OPERAND; private CalcOTTF(int operand) { OPERAND = operand; } int calculate() { // 피연산자를 반환 return OPERAND; } }
하지만 연산자마다 피연산자를 계산하는 방식이 다르기 때문에, 열거형에 추상 메서드 'calculrate(int operand)'를 선언하면 각 열거형 상수가 이 추상메서드를 반드시 구현해야함.
enum CalcOTTF { PLUS(1) { int calculate(int baseOperand) { return baseOperand + operand; } }, MINUS(2) { int calculate(int baseOperand) {return baseOperand - operand; }}, MULTIPLY(3) { int calculate(int baseOperand) {return baseOperand * operand; }}, DIVIDE(4) { int calculate(int baseOperand) {return baseOperand / operand; }}; abstract int calculate(int baseOperand); // 피연산자에 따른 계산을 해야하는 추상 메서드 protected final int OPERAND; // protected로 해야 열거형상수에서 각각 접근 가능 CalcOTTF(int operand) { OPERAND = operand; } public int getCalc() { return OPERAND; } } class EnumCalcOTTF { public static void main(String[] args) { System.out.println("PLUS = " + CalcOTTF.PLUS.getCalc(100)); System.out.println("MINUS = " + CalcOTTF.MINUS.getCalc(200)); System.out.println("MULTIPLY = " + CalcOTTF.MULTIPLY.getCalc(300)); System.out.println("DIVIDE = " + CalcOTTF.DIVIDE.getCalc(400)); } }
열거형에 대하여
열거형 상수가 다 Area객체
enum Area { ONE, TWO, THREE, FOUR } // Class로 구현해본다면 ? class Area { static final Area ONE = new Area("ONE"); static final Area TWO = new Area("TWO"); static final Area THREE = new Area("THREE"); static final Area FOUR = new Area("FOUR"); private String name; private Area(String name) { this.name = name; } }
추상클래스 Enum을 대충 따라해보고, 그 것을 Area Class가 상속해보자
/* abstract class Enum2<T> implements Comparable<T>로 했다면? * ordinal - t.ordinal()에서 에러가 남 * 이유 : 타입 T에 orinal()이 정의되어있는지 알 수 없기 때문임 */ abstract class Enum2<T extends Enum2<T>> implements Comparable<T> { // 객체 일련번호 static int id = 0; int ordinal; String name = ""; public int ordinal() { return ordinal; } MyEnum(String name) { this.name = name; ordinal = id++; // 객체를 생성할 때마다 id의 값을 증가 } public int compareTo(T t) { return ordinal - t.ordinal(); } } // Class로 구현해본다면 ? class Area { static final Area ONE = new Area("ONE"); static final Area TWO = new Area("TWO"); static final Area THREE = new Area("THREE"); static final Area FOUR = new Area("FOUR"); private String name; private Area(String name) { this.name = name; } }
테스트
abstract class Enum2<T extends Enum2<T>> implements Comparable<T> { static int id = 0; int ordinal; String name = ""; public int ordinal() { return ordinal; } Enum2(String name) { this.name = name; ordinal = id++; } public int compareTo(T t) { return ordinal - t.ordinal(); } } abstract class CalcOTTF extends Enum2 { static final CalcOTTF PLUS = new CalcOTTF("PLUS") { int calculate(int operand1, int operand2) { return operand1 + operand2; } }; static final CalcOTTF MINUS = new CalcOTTF("MINUS") { int calculate(int operand1, int operand2) { return operand1 - operand2; } }; static final CalcOTTF MULTIPLY = new CalcOTTF("MULTIPLY") { int calculate(int operand1, int operand2) { return operand1 * operand2; } }; static final CalcOTTF DIVIDE = new CalcOTTF("DIVIDE") { int calculate(int operand1, int operand2) { return operand1 / operand2; } }; abstract int calculate(int operand1, int operand2); protected CalcOTTF(String name) { super(name); } public String name() { return name; } public String toString() { return name; } } class TestABC { public static void main(String[] args) { CalcOTTF plus = CalcOTTF.PLUS; CalcOTTF plus2 = CalcOTTF.PLUS; int plusInt = plus.calculate(10, 20); CalcOTTF minus = CalcOTTF.MINUS; int minusInt = minus.calculate(10 , 20); CalcOTTF multiply = CalcOTTF.MULTIPLY; int multiplyInt = multiply.calculate(10, 20); CalcOTTF divide = CalcOTTF.DIVIDE; int divideInt = divide.calculate(10, 20); System.out.println("plus : " + plusInt + " minus : " + minusInt + " multiply : " + multiplyInt + " divide : " + divideInt); System.out.println(); System.out.println("plus : " + plus.name + ", " + plus.ordinal()); System.out.println("minus : " + minus.name + ", " + minus.ordinal()); System.out.println("multiply : " + multiply.name + ", " + multiply.ordinal()); System.out.println("divide : " + divide.name + ", " + divide.ordinal()); System.out.println(); System.out.println("plus==plus2 ? " + (plus == plus2)); System.out.println("plus.compareTo(multiply)=" + plus.compareTo(multiply)); } } /* plus : 30 minus : -10 multiply : 200 divide : 0 plus : PLUS, 0 minus : MINUS, 1 multiply : MULTIPLY, 2 divide : DIVIDE, 3 plus==plus2 ? true plus.compareTo(multiply)=-2 Process finished with exit code 0 */
반응형'Java' 카테고리의 다른 글
Lambda Expression 2. Function 패키지 (0) 2020.02.08 Lambda Expression 1. 람다식 (0) 2020.02.08 서버 통신 - Socket / UDP (0) 2020.02.07 Serializable (0) 2020.02.07 Java NIO (0) 2020.02.06