ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Enum(열거형)
    Java 2020. 2. 8. 14:35
    반응형

    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

    댓글

Designed by Tistory.