반응형

목표

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

 

학습할 것

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

enum 이란?

enum 열거형이라고 부르며, 서로 관련 있는 상수들을 여러 개 정의할 때 사용한다.

  • 클래스처럼 보이게 하는 상수(상수 목록을 담을 수 있는 데이터 타입)
  • 서로 관련 있는 상수들을 모아 심볼릭한 명칭의 집합으로 정의한 것
  • 특정한 변수가 가질 수 있는 값을 제한할 수 있다.(타임-세이프티(Type-Safety)를 보장할 수 있다.)
  • 각각의 enum 타입들은 오로지 JVM 내에서 하나만 만들어진다. 
  • enum의 값은 'equals'를 함수를 사용하여 값을 비교하는 것보다 '=='를 사용해서 비교하는게 더 효과적이다.(NullpointerException을 피할 수 있다.)

enum이 만들어진 이유

타입을 판별하지(Type Check) 못해 Runtime 시 타입으로 인한 문제가 발생하는 것이다.

아래 예시를 보면서 차근차근 알아보겠다.

 

과일 객체에 사과와 바나나 상수를 생성하고, 회사 객체에 apple, google을 상수로 생성하였다.

과일 객체의 APPLE과 회사의 APPLE은 비교조차 안되어야 다른 개념인데 같은 int 타입이기 때문에 비교가 되어서 같은 값이 된다. 심지어 1로 같기 때문에 '과일 사과와 회사 사과가 같다고?'가 출력된다.

 

위와 같은 문제는 서로 다른 객체를 만들어줌으로써 해결이 가능하다.

 

똑똑한 intellij가 잘못된 비교라고 표시해주고 있다.

하지만 위와 같은 상황에서도 문제는 발생한다.

 

switch case문에서는 사용자 정의 타입이 들어갈 수 없다.

위와 같은 문제들을 해결하기 위해서 JDK1.5 버전 이상부터는 enum이라는 개념이 추가되었다.

enum 정의하는 방법

정의하는 방법은 아래와 같다.

enum이라는 키워드를 사용하여 열거체를 정의하면 된다.

보통 열거체는 대문자를 사용한다.

 

 

enum을 정의해 보았다면 사용해보겠다.

사용하는 방법은 아래와 같다.

 

위에서 사용하지 못했던 switch case문도 정상적으로 사용가능하다.

enum에 정의된 상수들은 사용자 정의 타입이 아니라 enum type의 객체이기 때문이다.

 

enum의 특징

1. 객체를 만들 수 없다.

enum의 생성자는 private로 강제화되어있어 외부에서 객체를 생성할 수 없다.

상수라는 건 다른 값으로 할당되면 안되기 때문에 private로 강제화 된 것 같다.

 

2. enum 또한 class 이기 때문에 생성자와 메소드를 추가할 수 있다.

 

생성자를 이용해서 요일의 한글을 표시해주고 한글 요일을 반환해주는 메소드를 생성해보았다.

 

3. 생성자가 호출될 때 모든 상수 하나당 각각의 인스턴스로 만들어진다.

생성자 호출 시 값을 출력해보았다.

 

 

MONDAY 상수를 생성하였지만 모든 상수의 인스턴스가 생성되어서 출력되는 것을 볼 수 있다.

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

values() 메소드는 enum 안에 선언된 모든 상수들을 배열로 반환한다.

valueOf() 메소드는 enum 안에 존재하는 상수를 가져올 때 사용한다.

enum에 존재하지 않는 상수를 가져오려고 한다면 에러가 발생한다.

 

 

enum ordinal() 메소드

상수가 정의된 순서를 반환한다.

ordinal 메소드는 enum 내부에서 사용하기 위해 만든 메소드이지 프로그래머가 이 메소드 사용하는 것은 안티패턴이다.

스프링에서 enum을 사용하게 되면 @Enumerated 애노테이션을 사용하는 경우가 발생하게 되는데, 이때 @Enumerated(EnumType.STRING)으로 해야지 @Enumerated(EnumType.ORDINAL)을 사용하게 되면 나중에 시스템이 유지 보수를 하면서 enum에 상수값들이 중간에도 더 추가될 수도 있는데, 그럴 때마다 순서가 바뀌게 되어 매우 큰 문제가 발생할 수 있다.

 

java.lang.Enum

enum 클래스는 java.lang.Enum 클래스를 상속 받도록 되어 있다.

사용자가 따로 Enum 클래스를 상속을 정의해 주지 않았지만 byte 코드를 열어보면 아래와 같이 java/lang/Enum을 상속받고 있는 것을 볼 수 있다. 그러기 때문에 다중 상속을 지원하지 않는 JAVA에서는 enum 클래스는 별도의 상속을 받을 수 없다.

 

EnumMap

EnumMap 클래스는 Map 구현체 중 Enum type 을 키로 사용하는 클래스입니다.

EnumMap은 Map 인터페이스에서 키를 특정 enum 타입만을 사용하도록 하는 구현체 입니다. enum 은 ordinal 이라는 순차적인 정수값을 가지고 있습니다.
EnumMap 순차적인 정수값 내부 데이터를 Array에 저장합니다.
그러면 우리가 많이 사용하는 HashMap 처럼 해시를 만들고 해시 충돌(Hash Collision)에 대응하는 작업자체가 필요 없게 됩니다.

HashMap 과 비교해봤을 때, EnumMap은 성능상의 이점을 노릴 수 있습니다.

 

EnumSet

EnumSet은 열거형을 위한 set 인터페이스 구현체이다. 

enum class를 set으로 사용하게 된다면 아래와 같이 HashSet을 사용하여서 만들어서 사용하게 되면 나중에 상수 값이 추가가 될 때마다 HashSet에도 추가를 해줘야 한다. 하지만 EnumSet을 사용하게 되면 그럴 필요가 없다.

추가적으로 ordinal 값의 순서대로 요소가 저장된다.

EnumSet 추가적인 사용법

 

EnumSet  HashSet 을 비교했을 때, 보통 전자가 훨씬 빠르다. 값을 예측 가능한 순서로 저장하여, 각각의 계산에단 하나의 비트만 검사하면 되기 때문이다.

HashSet 과는 달리 정확한 버킷을 찾기 위해 해시 코드를 연산할 필요가 없으며, 또한 비트 벡터(bitvector)의 특성으로 인해 EnumSet 은 매우 작고 메모리를 덜 사용하므로 더 효율적이다.

728x90
반응형

+ Recent posts