[Java] 열거형

Hoplin·2023년 2월 15일
0

열거형이란

열거형이란 서로 연관된 상수들을 편리하게 선언하기 위한 것으로, 상수를 여러개 정의할때 유용하다. enum은 아래와 같은 형태로 작성한다

enum (name) {
	상수1,상수2,상수3...
}

열거형을 통해 연관된 상수들끼리 정의할 수 있다고 하였다. 이는 아래 예시를 참고한다. 카드의 종류인 Kind끼리의 상수와 Value끼리의 상수를 묶는다

enum Kind {
    CLOVER,HEART,DIAMOND,SPADE;
}

enum Value{
    ONE,TWO,THREE,FOUR;
}

class Card{
    {
        this.kind = Kind.CLOVER;
        this.value = Value.ONE;
    }
    private final Kind kind;
    private final Value value;
}

열거형에 정의된 상수를 사용하는 방법은 (열거형 이름).(상수명)형태이다. 일반적으로 클래스 멤버를 참조하는 방식과 동일하다. 열거형 상수간에는 ==연산자를 사용할 수 있으며, 대수 비교(>,<)는 불가능하다. 모든 열거형의 조상은 Enum클래스이고, Enum은 아래와 같이 기본적으로 Comparable인터페이스를 구현하였다. 그렇기에 대수 비교 대신 compareTo()를 사용할 수 있다.

switch-case조건식에서도 열거형을 사용할 수 있다. 다만 주의할 점은 열거형의 이름은 적지 않고 상수의 이름만 적어야 한다는 점이다.

void test(Value value){
        int counter = 0;
        switch (value){
            case ONE:
                counter+=1;
                break;
            case TWO:
                counter += 2;
                break;
            case THREE:
                counter += 3;
                break;
            case FOUR:
                counter += 4;
                break;
            default:
        }
    }

열거형에 몇가지 메소드들이 있다. 각각의 역할을 살펴보자

  • ordinal() : 열거형 상수가 정의된 순서를 반환하고, 0부터 시작된다
  • name() : 열거형 상수의 이름을 문자열로 반환한다.
  • getDeclaringClass() : 열거형의 Class객체를 반환한다
  • values() : 컴파일러에 의해 추가되는 메소드이다. 이 메소드는 모든 열거형이 가지고 있는것으로, 열거형의 모든 상수를 배열에 담아 반환한다.
  • valueOf(String name) : value()와 동일하게 컴파일러에 의해 추가된다. 열거형 상수의 이름으로 문자열 상수에 대한 참조를 얻을 수 있게 해준다.
  • static valueOf(Class<T> enumType, String name) : 지정된 열거형에서 name과 일치하는 열거형 상수를 반환한다.
static void test2(){
        Kind[] kinds = Kind.values();
        for(Kind kind : kinds){
            System.out.println("----------");
            System.out.println(kind.getDeclaringClass());
            System.out.println(kind.name());
            System.out.println(kind.ordinal());
            System.out.println("----------");
        }
        System.out.println(Kind.valueOf("SPADE"));
        System.out.println(Enum.valueOf(Value.class,"ONE"));
    }

열거형의 구조

열거형의 구조를 살펴보자. 열거형 상수의 하나하나는 해당 상수가 선언된 열거형의 객체가 된다. 예를 들어 아래와 같은 열거형이 있다고 가정하자.

enum Direction{
    EAST,SOUTH,WEST,NORTH
}

이를 클래스로 바꿔보면 아래와 동일한 의미가 된다

class Direction{
    static final Direction EAST = new Direction("East");
    static final Direction SOUTH = new Direction("SOUTH");
    static final Direction WEST = new Direction("WEST");
    static final Direction NORTH = new Direction("NORTH");

    private String value;
    private Direction(String value){
        this.value = value;
    }
}

열거형에 멤버 추가하기

위에서 봤듯이,열거형 상수 하나하나는 자신이 선언된 열거형의 객체라는것을 알았다. 어떻게 보면 클래스와, 클래스 인스턴스인 것이다.클래스에 메소드를 선언하듯이, 메소드 멤버를 선언해 줄 수 있고, 각 상수별로 가지고 있는 값 또한 지정할 수 있다.

상수에 값 지정하기

상수의 값이 불연속적이거나, 여러개를 가져야 하는 경우에는 상수 일므 옆에 괄호를 함께 적어준다.

enum Test{TEST1(1),TEST(2),TEST(3),TEST(4)}

이 상태로 실행하면 Expected 0 arguments but found 1이라는 오류가 나오게 된다 .이 이유는, 상수에 값을 지정하는 경우, 값을 저장할 수 있는 변수와 생성자를 추가해 주어야 한다. 그리고 멤버를 선언할때는, 열거형 상수 마지막에 ;을 붙여 구분해 주어야 한다.

enum Direction2{
    EAST(1),SOUTH(2),WEST(3),NORTH(4);
    private final int value;
    Direction2(int value){
        this.value = value;
    }
}

값을 저장하는 인스턴스 변수가 꼭 final일 필요는 없지만, 상수라는 의미를 명백히 하고자 final을 붙였다. 한가지 더 알아야할 것은, 열거형의 생성자는 암묵적으로 private 접근제어자를 가지고 있다. 그렇기 때문에, new를 통해 인스턴스를 만들지는 못한다.

열거형 상수는 값을 하나 이상 가질 수 있으며, 하나를 가질때와 마찬가지로, 그에 상응하는 값을 저장하는 인스턴스 변수와 생성자가 필요하다

enum Direction2{
    EAST(1,">"),SOUTH(2,"v"),WEST(3,"<"),NORTH(4,"^");
    private final int value;
    private final String direction;
    Direction2(int value, String direction){
        this.value = value;
        this.direction = direction;
    }

    public String getDirection() {
        return direction;
    }

    public int getValue() {
        return value;
    }

    @Override
    public String toString() {
        return this.value + " " + this.direction;
    }
}

열거형에 추상 메소드 추가하기

열거형 메소드로 추상 메소드를 선언해 줄 수 있다. 예를 들어, 과일별로 기본 가격과 무게가 있고, 그에 따라 계산 방법이 다르다고 가정고, 계산하는 추상 메소드 이름은 calculate라고 가정한다. 그러면 아래와 같이 열거형을 작성해 줄 수 있다.

enum Fruits{
    APPLE(3000), ORANGE(4000), GRAPE(5000);

    protected final int price;
    Fruits(int price){
        this.price = price;
    }

    public int getPrice() {
        return price;
    }
    abstract int calculate(int weight);
}

추상메소드는 알다싶이, 인스턴스로 사용할 수 없다. 그렇기 때문에 구현을 해주어야 한다. 어떻게 구현을 해서 사용할까? 앞에서 봤듯이, 열거형의 각각 상수는 모두 열거형의 인스턴스이다. 그렇기 때문에 각각의 상수에 구현을 해주면 된다. 구현 방식은 전에 익명 클래스인스턴스를 만들듯이 선언하면 된다.

enum Fruits{
    APPLE(3000){
        @Override
        int calculate(int weight) {
            return this.price * weight;
        }
    }, ORANGE(4000){
        @Override
        int calculate(int weight) {
            return this.price * weight;
        }
    }, GRAPE(5000){
        @Override
        int calculate(int weight) {
            return this.price * weight;
        }
    };

    protected final int price; // protected를 해야 추상메소드 구현에서 접근할 수 있다.
    Fruits(int price){
        this.price = price;
    }

    public int getPrice() {
        return price;
    }
    abstract int calculate(int weight);
}

주의깊게 볼 점은, 상수의 값을 저장하는 priceprivate에서 protected로 변경되었다는 점이다. 이렇게 바뀐 이유는, 익명함수 형태로 메소드를 구현하게 된다면, 상속을 받는 원리랑 동일하기 때문에, protected로 지정하여, 익명클래스 인스턴스 정의할 때 사용하게끔 하는것이다.
아래 코드는 익명함수 예시 코드이다.

abstract class abctest{
    protected final int t1 = 10;
    abstract void tt();
}


// main 함수
public static void main(String[] args){
        abctest a1 = new abctest() {
            @Override
            void tt() {
                System.out.println(this.t1);
            }
        };
    }
profile
더 나은 내일을 위해 오늘의 중괄호를 엽니다

0개의 댓글