자바의 열거형에 대해 학습하세요.

  • enum 정의하는 방법
  • enum이 제공하는 메소드 (values()와 valueOf())
  • java.lang.Enum
  • EnumSet

enum 정의하는 방법

Enum이란?

  • 열거형
  • 서로 관련된 상수를 편리하게 선언하기 위한 것으로 상수를 여러 개 정의할 때 사용
  • 정의된 것 이외의 값은 허용하지 않는다. (=허용 가능한 값들을 제한할 수 있다.)
  • 리팩토링 시 내용 추가가 필요하더라도, Enum 코드 외에 수정할 필요가 없다.
  • jdk 1.5부터 추가
enum 열거형이름 {상수명1, 상수명2, 상수명3, ...}
package enumex;

public class EnumTest {
    enum Number {one, two, three, four}

    public static void main(String[] args) {
        EnumTest e = new EnumTest();
        System.out.println(Number.two);
        System.out.println(Number.two.ordinal()); // enum의 순서를 의미
    }
}

자바 Enum의 특징

  • Enum에 정의된 상수들은 해당 enum type의 객체이다.
enum Fruit { APPLE, PEACH, BANANA }

class Fruit {
    public static final Fruit APPLE = new Fruit("APPLE");
    public static final Fruit PEACH = new Fruit("PEACH");
    public static final Fruit BANANA = new Fruit("BANANA");
    
    private String name;
    
    private Fruit(String name) {
        this.name = name;
    }
}
// 위와 아래는 같은 의미이다.
  • 생성자와 메서드를 추가할 수 있다.
    enum과 클래스를 정의하는 방법은 거의 비슷하지만 몇 가지 차이가 있다.
    1. class 대신 enum 이라고 적는다.
    2. 첫 줄에는 열거할 상수의 이름을 선언한다. (이름은 대문자가 관례이며 상수는 콤마로 구분, 제일 마지막 상수는 세미콜론을 붙여야 한다.)
    3. enum의 생성자의 접근제어자는 private이므로 외부에서 상수를 추가할 수 없다.
    4. 열거형의 멤버 중 하나를 호출하면, 열거된 모든 상수의 객체가 생성된다.
    5. 상수 하나당 각각의 인스턴스가 만들어지며 모두 public static final 이다.
enum Fruit {
    APPLE, PEACH, BANANA;    // 열거할 상수의 이름 선언, 마지막에 ; 을 꼭 붙여야한다.

    Fruit() {
        System.out.println("생성자 호출 " + this.name());
    }
}

public class EnumDemo {

    public static void main(String[] args) {
        Fruit apple = Fruit.APPLE;
    // Fruit grape = new Fruit();
    // 에러 발생. 열거형의 생성자의 접근제어자는 항상 private이다.
    }
}

// 생성자 호출 APPLE
// 생성자 호출 PEACH
// 생성자 호출 BANANA
  • 생성자를 이용하여 상수에 데이터를 추가할 수 있다.
package enumex;

public class EnumTest {
    enum Number {
        one(1), two(2), three(3), four(4);

        private int i;

        Number(int i) {
            this.i = i;
        }

        public int getI() {
            return i;
        }
    }

    public static void main(String[] args) {
        System.out.println(Number.two.getI());
    }
}

// 2
  • 추상 메서드를 선언하여 각 상수 별로 다르게 동작하는 코드를 구현할 수 있다.
    enum을 상속받는 익명 클래스를 선언하는 것
package enumex;

public class EnumTest{
    enum Number {
        one(1){
            @Override
            public void getI() {
                System.out.println("one : "+ Number.one.i);
            }
        },
        two(2){
            @Override
            public void getI() {
                System.out.println("two : "+ Number.two.i);
            }
        },
        three(3){
            @Override
            public void getI() {
                System.out.println("three : "+ Number.three.i);
            }
        },
        four(4){
            @Override
            public void getI() {
                System.out.println("four : "+ Number.four.i);
            }
        };

        private int i;

        Number(int i) {
            this.i = i;
        }

        public void getI() {
        }

    }

    public static void main(String[] args) {
        Number.one.getI();
        Number.two.getI();
        Number.three.getI();
        Number.four.getI();
    }
}

// one : 1
// two : 2
// three : 3
// four : 4
  • 상수 간의 비교가 가능하다.
    == 비교 연산자 사용 가능
    >, < 비교 연산자는 사용 불가 / compareTo()를 사용한다.
Direction dir;

if (dir == Direction.WEST) {
	...
} else if (dir > Direction.WEST) {
	// 에러. 열거형 상수에 비교연산자는 사용 불가능
} else if (dir.compareTo(Direction.WEST) > 0) {
	// compareTo()는 가능
}

enum이 제공하는 메소드 (values()와 valueOf())

메서드 이름 설명
toString() 해당 상수의 이름을 문자열로 반환
name() 해당 상수의 이름을 문자열로 반화
compareTo() 정렬의 기준을 위한 메서드
비교 대상보다 순서가 빠르면 -1, 같으면 0, 느리면 1을 반환
정렬 순서는 상수가 선언된 순서가 디폴트로 지정되어 있다.
ordinal() 상수의 선언 순서에 따른 인덱스(zero based)값을 반환
Enum 안에는 private final int ordinal; 이 정의되어 있고 이를 사용한다.
valueOf() 인자로 받은 이름과 같은 Enum값으로 반환
Direction d = Direction.valueOf("WEST");
Direction.WEST == Direction.valueOf("WEST"); // true 반환
values() 선언된 모든 Enum값을 순서대로 배열에 담아서 반환
Direction[] arr = Direction.values();
package enumex;

public class EnumTest{
    enum Number {
        one(1){
            @Override
            public void getI() {
                System.out.println("one : "+ Number.one.i);
            }
        },
        two(1){
            @Override
            public void getI() {
                System.out.println("two : "+ Number.two.i);
            }
        },
        three(3){
            @Override
            public void getI() {
                System.out.println("three : "+ Number.three.i);
            }
        },
        four(4){
            @Override
            public void getI() {
                System.out.println("four : "+ Number.four.i);
            }
        };

        private int i;

        Number(int i) {
            this.i = i;
        }

        public void getI() {
        }

    }

    public static void main(String[] args) {
        Number[] values = Number.values();
        for (Number value : values) {
            System.out.println(value);
        }

        System.out.println();
        System.out.println(Number.valueOf(Number.class, "one"));
    }
}

// one
// two
// three
// four

// one

toString()과 name()의 차이

  • toString()과 name()은 같은 값을 반환한다.
  • name()은 final로 선언된 메서드로 오버라이딩이 불가능하다.
  • toString()은 일반적인 Object 클래스의 메서드로 오버라이딩이 가능하다.

java.lang.Enum

  • java.lang에 포함된 Enum은 모든 자바 열거형의 조상이다.
  • 모든 열거형은 Enum 클래스를 상속받기 때문에 enum type은 별도의 상속을 받을 수 없다.
  • Enum 클래스는 abstract 이기에 인스턴스를 생성할 수 없다.
  • toString을 제외한 대부분의 메서드는 final로 선언되어 있기 때문에 오버라이딩 할 수 없다.
public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {
    /**
     * The name of this enum constant, as declared in the enum declaration.
     * Most programmers should use the {@link #toString} method rather than
     * accessing this field.
     */
    private final String name;

    /**
     * Returns the name of this enum constant, exactly as declared in its
     * enum declaration.
     *
     * <b>Most programmers should use the {@link #toString} method in
     * preference to this one, as the toString method may return
     * a more user-friendly name.</b>  This method is designed primarily for
     * use in specialized situations where correctness depends on getting the
     * exact name, which will not vary from release to release.
     *
     * @return the name of this enum constant
     */
    public final String name() {
        return name;
    }
    ...

EnumSet

set

  • 데이터를 중복해서 저정할 수 없다.
  • 저장 순서가 보장되지 않는다.
  • 대표 클래스는 HashSet, TreeSet, LinkedHashSet 등이 있다.
  • 공통적으로 사용하는 메서드는 add, iterator, size, remove, clear 등이 있다.

EnumSet

  • Set 인터페이스를 기반으로 하면서 Enumeration type을 사용하는 방법
메서드 설명
allOf(Class elementType) 인자로 들어온 enum을 그대로 enum set 생성
complementOf(EnumSet s) 인자로 들어온 enum set에서 없는 요소들로만 다시 enum set 생성
of(E e1, E e2, E e3, E e4, E e5) 초기값으로 지정한 값들로 enum set 생성
range(E from, E to) 처음과 끝을 입력하면 그 사이에 있는 값들로 Enum set 생성
package enumex;

import java.util.EnumSet;

enum Alphabet {A, B, C, D, E}
public class EnumSetTest {
    public static void main(String[] args) {
        EnumSet<Alphabet> set1, set2, set3, set4;

        set1 = EnumSet.allOf(Alphabet.class);
        set2 = EnumSet.of(Alphabet.A, Alphabet.B, Alphabet.C, Alphabet.D, Alphabet.E);
        set3 = EnumSet.complementOf(set1);
        set4 = EnumSet.range(Alphabet.B,Alphabet.D);

        System.out.println(set1);
        System.out.println(set2);
        System.out.println(set3);
        System.out.println(set4);
    }
}

// [A, B, C, D, E]
// [A, B, C, D, E]
// []
// [B, C, D]

EnumMap

  • Key와 Value를 가지고 있다.
  • Enum 상수는 이미 그 자체로 고유한 싱글턴 객체이므로 해싱처리를 해 줄 필요가 없다.
  • 순서를 기억한다.
메서드 설명
put(K key, V value) Key, Value 값을 받아 내부 배열에 저장한다.
putAll(Map<? extends K, ? extends V>m 이미 생성된 적 있는 Map 객체를 내부 배열에 저장한다.
size() Enummap의 Key와 Value 쌍의 갯수를 반환
get(Object key) key를 통해 value의 값을 반환한다.
containsKey(Object key) EnumMap에 특정 key가 존재하는지 확인 후 boolean을 반환
containsValue(Object value) EnumMap에 특정 value 값이 존재하는지 확인 후 boolean을 반환
replace(K key, V value) 기존 key에 있던 value값을 바꾼다.
replace(K keym, V oldValue, V newValue) 안정성을 보장해주는 방법으로 key의 이전 value 값이 맞으면 현재값으로 변경해준다.
package enumex;

import java.util.EnumMap;

enum Alphabet2 {A, B, C, D, E}
public class EnumMapTest {
    public static void main(String[] args) {
        EnumMap<Alphabet2,String> enumMap = new EnumMap<Alphabet2, String>(Alphabet2.class);

        enumMap.put(Alphabet2.A,"AAA");
        enumMap.put(Alphabet2.A,"AAA1");
        enumMap.put(Alphabet2.B,"BBB");
        enumMap.put(Alphabet2.C,"CCC");
        enumMap.put(Alphabet2.D,"DDD");

        System.out.println(enumMap.size()); // 사이즈 출력
        System.out.println();

        System.out.println(enumMap); // enumMap 출력
        System.out.println();

        System.out.println(enumMap.get(Alphabet2.C)); // 키값으로 값 출력
        System.out.println();

        System.out.println(enumMap.containsKey(Alphabet2.A)); // 키값을 포함하는지 확인
        System.out.println();

        System.out.println(enumMap.containsValue("AAA1")); // 값을 포함하는지 확인
        System.out.println();

        EnumMap<Alphabet2,String> enumMapTest = new EnumMap<Alphabet2, String>(Alphabet2.class);
        enumMapTest.putAll(enumMap); // 정의된 enummap을 받음
        System.out.println(enumMapTest);
        System.out.println();

        enumMapTest.replace(Alphabet2.A,"AAA1","AAA2"); // 특정 키 값이 주어진 값과 같으면 다른값으로 변경
        System.out.println(enumMapTest.get(Alphabet2.A));

    }

}

// 4
// {A=AAA1, B=BBB, C=CCC, D=DDD}
// CCC
// true
// true
// {A=AAA1, B=BBB, C=CCC, D=DDD}
// AAA2

'Back-end > java' 카테고리의 다른 글

스터디 13주차 - I/O  (0) 2021.07.07
스터디 12주차 - 어노테이션  (0) 2021.07.06
스터디 10주차 - 멀티쓰레드 프로그래밍  (0) 2021.07.02
스터디 9주차 - 예외 처리  (0) 2021.07.01
스터디 8주차 - 인터페이스  (0) 2021.07.01

+ Recent posts