반응형

열거형의 목적

열거형은 자바 5에서 추가된 자료형으로, 의미가 부여된 이름을 갖는 상수의 선언에 그 목적이 있습니다.

 

열거형 학습에 앞서, 자바 5 이전에는 의미가 부여된 이름을 갖는 상수를 어떻게 선언했는지 다음 코드로 확인해보겠습니다.

public interface Scale {
    int DO = 0;
    int RE = 1;
    int MI = 2;
    int FA = 3;
    int SO = 4;
    int RA = 5;
    int TI = 6;
}

public class InterfaceBaseConst {
    public static void main(String[] args) {
        int sc = Scale.DO;

        switch (sc) {
            case Scale.DO:
                System.out.println("음 이름 : 도");
                break;
            case Scale.RE:
                System.out.println("음 이름 : 레");
                break;
            case Scale.MI:
                System.out.println("음 이름 : 미");
                break;
            case Scale.FA:
                System.out.println("음 이름 : 파");
                break;
            default:
                System.out.println("음 이름 : 솔, 라, 시");
        }
    }
}

출력 결과값은 다음과 같습니다.

음 이름 : 도

인터페이스 내에 선언된 변수는 public, static, final 이 선언된 것으로 간주합니다.

따라서 인터페이스 Scale 을 통해 7개의 상수가 선언되었습니다.

 

인터페이스 Scale 은 음계를 표현한 상수들을 담고 있습니다.

이 경우 중요한 것은 상수의 값이 아니라 상수의 이름입니다.

즉 상수의 값이 바뀌어도 이름이 바뀌지 않는다면 코드에 아무런 영향을 주지 않습니다.

그리고 이렇게 연관된 상수들을 하나의 인터페이스로 묶어서 선언하는 것이 자바 5 이전에 사용하던 방법입니다.

그러나 이 방법에는 문제가 하나 있는데, 그 문제점을 다음 코드에서 확인하겠습니다.

public interface Animal {
    int Dog = 1;
    int CAT = 2;
}

public interface Person {
    int MAN = 1;
    int WOMAN = 2;
}

public class NonSafeConst {
    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;
        }
    }
}

출력 결과값은 다음과 같습니다.

남자입니다.
남자입니다.

Person.MAN 도 값이 1이고 Animal.DOG 도 값이 1이기 때문에,

위와 같은 실수를 범해도 컴파일 오류는 물론 실행 오류도 발생하지 않습니다.

그 문제점을 해결하기 위해 자바 5에서 열거형이 소개되었습니다.


열거형의 정의

열거형은 다음과 같이 정의합니다.

그 안에 위치한 이름들을 가리켜 '열거형 값' 이라고 합니다.

public enum Scale {
    DO, RE, MI, FA, SOL, RA, THI
}

 

열거형은 클래스와 성격이 유사합니다.

따라서 다음과 같이 참조변수의 선언도 가능합니다.

단 이렇게 선언된 참조변수는 해당 열거형 내에 선언된 '열거형 값'만 대입이 가능합니다.

        Scale scale = Scale.DO;

 

기본적으로 열거형 값은 Scale.DO 와 같이 표현하지만,

case 문에서는 표현의 간결함을 위해 Do 와 같이 열거형 값의 이름만 명시하기로 약속되어 있습니다.

다음은 열거형 이전의 코드를 열거형 이후의 코드로 수정한 코드입니다.

public enum Scale {
    DO, RE, MI, FA, SOL, RA, THI
}

public class SimpleEnum {
    public static void main(String[] args) {
        Scale scale = Scale.DO;
        System.out.println(scale);

        switch (scale) {
            case DO:
                System.out.println("음 이름 : 도");
                break;
            case RE:
                System.out.println("음 이름 : 레");
                break;
            case MI:
                System.out.println("음 이름 : 미");
                break;
            case FA:
                System.out.println("음 이름 : 파");
                break;
            default:
                System.out.println("음 이름 : 솔, 라, 시");
        }
    }
}

출력 결과값은 다음과 같습니다.

DO
음 이름 : 도

 

그렇다면 열거형 이전의 문제점이 해결되었는지 아래의 코드로 확인해보겠습니다.

public enum Animal {
    DOG, CAT
}

public enum Person {
    MAN, WOMAN
}

public class SafeEnum {
    public static void main(String[] args) {
        who(Person.MAN);
        who(Animal.DOG);
        Animal animal = Person.MAN;
    }

    public static void who(Person who) {
        switch (who) {
            case MAN:
                System.out.println("남자입니다.");
                break;
            case WOMAN:
                System.out.println("여자입니다.");
                break;
        }
    }
}

 

열거형 Animal 과 Person 이 정의되었습니다.

코드를 실행시켜보지만 아래의 코드에서 자료형 불일치로 인한 컴파일 오류가 발생합니다.

따라서 앞서 소개한 자바 5 이전의 문제점이 열거형을 사용할 경우 발생하지 않습니다.

        who(Animal.DOG);
        Animal animal = Person.MAN;

클래스 내에 열거형 정의

특정 클래스 내에서만 사용하고자 하는 열거형 값이 있다면, 아래의 코드와 같이 정의하면 됩니다.

public class Customer {

    enum Gender {
        MALE, FEMALE
    }

    private String name;
    private Gender gender;

    public Customer(String name, String gender) {
        this.name = name;

        if ("man".equals(gender)) {
            this.gender = Gender.MALE;
        } else {
            this.gender = Gender.FEMALE;
        }
    }

    @Override
    public String toString() {
        if (gender == Gender.MALE) {
            return "Mr " + name;
        } else {
            return "Ms " + name;
        }
    }
}

public class InnerEnum {
    public static void main(String[] args) {
        Customer customer1 = new Customer("Park", "man");
        Customer customer2 = new Customer("Han", "woman");

        System.out.println(customer1);
        System.out.println(customer2);
    }
}

 

출력 결과값은 다음과 같습니다.

Mr Park
Ms Han

열거형 값의 정체

진행하기 전에 먼저 다음과 같이 클래스 정의가 가능함을 소개하고자 합니다.

public class Person {
    public static final Person MAN = new Person();
    public static final Person WOMAN = new Person();

    @Override
    public String toString() {
        return "I am Person";
    }
}

public class InClassInst {
    public static void main(String[] args) {
        System.out.println(Person.MAN);
        System.out.println(Person.WOMAN);
    }
}

 

출력 결과값은 다음과 같습니다.

I am Person
I am Person

 

위 코드에서 보이듯이 Person 클래스 내에서 Person 형 참조변수를 선언하는 것도, Perosn 인스턴스를 생성하는 것도 가능합니다.

그럼 다음 코드를 보겠습니다. 이 코드에서는 열거형 값이 해당 자료형의 인스턴스라는 사실을 알려줍니다.

public enum Person {
    MAN, WOMAN;

    @Override
    public String toString() {
        return "Enum Person Object";
    }
}

public class EnumConst {
    public static void main(String[] args) {
        System.out.println(Person.MAN);
        System.out.println(Person.WOMAN);
    }
}

 

출력 결과값은 다음과 같습니다.

Enum Person Object
Enum Person Object

 

위 결과에서 확인할 수 있는 점은 다음과 같습니다.

  1. 열거형은 클래스입니다.
    모든 열거형은 java.lang.Enum<E> 클래스를 상속합니다.
    그리고 Enum<E> 는 Object 클래스를 상속합니다.
    이러한 측면에서 볼 때 열거형은 클래스입니다.
  2. 열거형의 값은 참조변수 입니다.
    이에 대한 증거로 출력 결과값에서 toString 메소드가 호출되었음을 보면 알 수 있습니다.

열거형의 정의에도 생성자가 따로 없다면 디폴트 생성자가 삽입됩니다.

다만 이 생성자는 private으로 선언되어 직접 인스턴스를 생성하는 것이 불가능할 뿐입니다.

이번에는 열거형 생성자를 정의해봄으로서, 열거형 값의 정체를 더 알아보겠습니다.

public enum Person {
    MAN, WOMAN;

    private Person() {
        System.out.println("Person 생성자 호출");
    }

    @Override
    public String toString() {
        return "Enum Person Object";
    }
}

public class EnumConstructor {
    public static void main(String[] args) {
        System.out.println(Person.MAN);
        System.out.println(Person.WOMAN);
    }
}

 

출력 결과값은 다음과 같습니다.

Person 생성자 호출
Person 생성자 호출
Enum Person Object
Enum Person Object

 

위 코드에서는 두 개의 열거형 값이 존재하기에 두 번의 생성자 호출이 이뤄졌습니다.

따라서 열거형 값의 정체는 다음과 같이 표현할 수 있습니다.

열거형 값은 생성자가 private이라 실제 컴파일은 되지 않습니다.

public static final Person MAN = new Person();
public static final Person WOMAN = new Person();

열거형의 생성자 정의 방법과 호출 방법

public enum Person {
    MAN(29),
    WOMAN(25);

    int age;

    private Person(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" + "age=" + age + '}';
    }
}

public class EnumParamConstructor {
    public static void main(String[] args) {
        System.out.println(Person.MAN);
        System.out.println(Person.WOMAN);
    }
}

 

출력 결과값은 다음과 같습니다.

열거형의 생성자는 무조건 private 으로 선언해야 합니다.

그리고 열거형 값의 선언에서 아래의 코드와 같이 소괄호를 통해서 생성자에 인자를 전달할 수 있습니다.

Person{age=29}
Person{age=25}

 

결론은 간단합니다.

열거형도 Object 클래스를 상속하는 일종의 클래스입니다.

따라서 생성자는 물론, 인스턴스 변수와 메소드 둘 다 가질 수 있습니다.

다만 모든 생성자를 private으로 선언해야 하기 때문에 열거형 값이 유일한 인스턴스 생성 방법이라는 차이가 있을 뿐입니다.

 

 

오늘도 좋은 하루 보내세요.

감사합니다.

반응형

'JAVA > 기본 개념' 카테고리의 다른 글

#11 Optional  (0) 2022.09.05
#5 추상 클래스 (Abstract Class)  (0) 2022.08.24
#8 Object 클래스  (0) 2022.08.23
#4 예외처리(Exception Handling)  (0) 2022.08.22
#3 인터페이스  (0) 2022.08.22

+ Recent posts