관리 메뉴

나만을 위한 블로그

[JAVA] enum(열거형)이란? 본문

JAVA

[JAVA] enum(열거형)이란?

참깨빵위에참깨빵 2021. 12. 4. 13:35
728x90
반응형

안드로이드 공부를 하다보면 깃허브에서 예제 프로젝트를 찾아보기 마련인데, 그러다 보면 가끔씩 이런 형태의 코드를 볼 수 있다.

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

이건 뭘까? 대문자로만 쓰여져 있는 걸 보니 상수인가? 근데 상수라고 치자니 왼쪽 위의 enum이란 글자가 신경쓰인다. 상수에는 보통 저런 키워드가 붙지 않으니까.

이번 포스팅에 쓸 것은 저 enum이란 것이다.

 

먼저 enum은 "Enumeration"의 약자다. Enumeration은 "열거, 목록, 일람표"라는 뜻도 갖고 있다. 하지만 보통 한글로는 열거형이라고 부르기 때문에, 열거의 뜻이 무엇인지도 확인해보자.

여러 가지 예나 사실을 낱낱이 죽 늘어놓음

 

기준이 있는지는 모르겠지만, 아무튼 어떤 여러가지를 생선 가게 판매대마냥 늘어놓는다는 게 열거라는 건 알겠다.

그럼 열거형은 여러 가지를 죽 늘어놓은 형태라서 열거형이라고 부르는 건가? 만약 그렇다고 한다면 자바의 열거형은 무엇일까?

 

https://ko.wikipedia.org/wiki/%EC%97%B4%EA%B1%B0%ED%98%95

 

열거형 - 위키백과, 우리 모두의 백과사전

컴퓨터 프로그래밍에서 열거형(enumerated type, enumeration), 이넘(enum), 팩터(factor ← R 프로그래밍 언어와 통계학의 범주형 변수에서 부르는 명칭)는 요소, 멤버라 불리는 명명된 값의 집합을 이루는

ko.wikipedia.org

열거형, 이넘(enum)은 요소, 멤버라 불리는 명명된 값의 집합을 이루는 자료형이다. 열거자 이름들은 일반적으로 해당 언어의 상수 역할을 하는 식별자다. 일부 열거자 자료형은 언어에 기본 소속되어 있을 수 있다.

 

http://tutorials.jenkov.com/java/enums.html

 

Java Enums

A Java Enum is a type used specifically to represent sets of constants. A Java Enum can be used instead of static final int or String variables otherwise used to represents constants in Java applications. This Java Enum tutorial explains how to create and

tutorials.jenkov.com

자바 Enum은 상수 컬렉션을 정의하는 데 쓰이는 특수한 자바 유형(type)이다. 더 정확하게 말하면 enum은 특수한 자바 클래스다. 열거형에는 상수, 메서드 등이 포함될 수 있으며 자바 5부터 추가되었다

 

https://www.w3schools.com/java/java_enums.asp

 

Java Enums

W3Schools offers free online tutorials, references and exercises in all the major languages of the web. Covering popular subjects like HTML, CSS, JavaScript, Python, SQL, Java, and many, many more.

www.w3schools.com

enum은 상수 그룹(final 변수처럼 바꿀 수 없는 변수)을 나타내는 특수한 "클래스"다. 열거형을 만들려면 class, interface 대신 enum 키워드를 사용하고 상수를 쉼표로 구분한다. 또한 대문자여야 한다.

 

대문자로 써서 쉼표로 구분하고 enum 키워드를 붙이면 열거형이 완성되는 것 같다.

그런데 이 enum은 언제 쓰는 것인가? 무슨 문제가 있어서 enum이란 개념이 생긴 것인가? 그리고 이걸 써서 얻는 장점은 뭐가 있을까?

 

https://heepie.me/32

 

[2017.08.21] 12. 왜 Enum을 사용할까?

Enum은 Enumerated Type으로 '서로 연관된 상수 값들의 집합'이다. (cf. 배열은 서로 연관된 변수 값들의 집합 / 왜 배열을 사용할까? - http://heepie.tistory.com/2) Enum의 등장 배경 1. 복잡한 값 -> 단순한..

heepie.me

장점
1. 코드의 양이 줄었다
2. 인스턴스 생성과 상속 시도 시 컴파일 에러
3. enum이란 키워드로 열거형의 의도를 명확히 드러냄

 

2번째 장점은 중간에 말이 끊겨서 뭘 말하고자 하는지 모르겠다. 일단 컴파일 에러를 없앤다는 의미로 생각하고 넘긴다.

 

https://techblog.woowahan.com/2527/

 

Java Enum 활용기 | 우아한형제들 기술블로그

{{item.name}} 안녕하세요? 우아한 형제들에서 결제/정산 시스템을 개발하고 있는 이동욱입니다. 이번 사내 블로그 포스팅 주제로 저는 Java Enum 활용 경험을 선택하였습니다. 이전에 개인 블로그에 E

techblog.woowahan.com

개발을 진행할 때 Enum을 통해 얻는 기본적인 장점들은 아래와 같다.
- 문자열과 비교해 IDE의 적극적 지원을 받을 수 있다(자동완성, 오타 검증, 텍스트 리팩토링 등)
- 허용 가능한 값들을 제한할 수 있다
- 리팩토링 시 변경 범위가 최소화된다 (내용의 추가가 필요하더라도 Enum 코드 외에 수정할 필요가 없다)

이 장점들은 모든 언어들의 Enum에서 얻을 수 있는 공통된 장점이다. 하지만 자바의 Enum은 이보다 더 많은 장점을 갖고 있다
C/C++의 경우 Enum은 결국 int 값이지만 자바의 Enum은 완전한 기능을 가진 클래스기 때문이다
예제로 그 장점들을 하나씩 소개한다.

1. 데이터들 간 연간관계 표현
2. 상태, 행위를 한 곳에서 관리
3. 데이터 그룹 관리

(중략)...가장 실감했던 장점은 문맥(Context)를 담는다는 것이다. A라는 상황에서의 "a"와 B라는 상황에서 "a"는 같은 문자열 "a"지만 전혀 다른 의미다. 문자열은 이를 표현할 수 없지만 Enum은 이를 표현할 수 있다. 이로 인해 실행되는 코드를 이해하기 위해 추가로 뭔가를 찾아보는 행위를 최소화할 수 있게 됐다
Enum을 사용하는 데 가장 큰 허들은 "변경이 어렵다"란 의견이다. 코드를 추가, 변경해야 하는 일이 빈번하다면 매번 Enum 코드를 변경, 배포하는 것보다 관리자 페이지에서 관리자가 직접 변경하는 게 편리할 수 있다고 생각한다...(중략)

 

https://www.javapedia.net/Enum/1685

 

Benefits of using Enums in Java.

Enum provides type-safe; Enum variable can be assigned only with predefined enum constants otherwise it will issue compilation error. Enum can be used in switch-case Java control statement. Enum has its own namespace. Enum constants can hold values specifi

www.javapedia.net

enum은 유형 안전(type safe)을 제공한다. 열거형 변수는 미리 정의된 열거형 상수로만 할당할 수 있다. 그렇지 않으면 컴파일 에러가 발생한다
enum은 switch문에서 사용할 수 있다
자체 네임 스페이스가 있다
enum 상수는 생성 시 지정된 값을 가질 수 있다
enum은 본질적으로 Thread safe인 싱글톤 패턴을 생성하는 데 도움이 될 수 있다
enum을 정적 팩토리 메서드에 대한 인수로 전달하면 메서드 유형이 안전하다(makes method type safe)

 

https://crunchify.com/why-and-for-what-should-i-use-enum-java-enum-examples/

 

Beginner's Guide to Java eNum - Why and for What should I use Enum? Java Enum Examples • Crunchify

Enums are lists of constants like unchangeable variables. Have you heard of Final keyword? It's like that. When you need a predefined list of values which

crunchify.com

어떤 종류의 숫자 또는 텍스트 데이터를 나타내는 미리 정의된 값 목록이 필요한 경우 열거형을 사용해야 한다. 예를 들어 체스 게임에서 다양한 유형의 말을 열거형으로 나타낼 수 있다. 변수(특히 메서드 매개변수)가 가능한 값의 작은 집합에서 하나만 가져올 수 있는 경우 항상 열거형을 써야 한다. 정수(또는 문자열 코드) 대신 열거형을 사용하는 경우, 컴파일 시간 검사를 늘리고 잘못된 상수를 전달하는 오류를 방지하고 어떤 값을 사용할 수 있는지 문서화한다

 

그럼 이 enum을 어떻게 사용하는지 간단한 예제로 확인한다.

enum을 쓰는 방법은 크게 2가지다. 클래스 내부에 선언하고 쓰던가 클래스 밖에서 선언하고 쓰던가 둘 중 하나다.

위에서 말한대로 4가지 계절을 정의한 enum을 선언해본다.

 

public enum Season {
    SPRING,
    SUMMER,
    FALL,
    WINTER
}
public class Main {
    public static void main(String[] args) {
        //
    }

    private enum Season {
        SPRING,
        SUMMER,
        FALL,
        WINTER
    }

}

 

이게 끝이다. 별 거 없다. 만약 클래스 안에 만드는 게 아니라 밖에 만들고 싶다면 자바 파일을 생성할 때 enum 키워드를 선택해주면 된다.

 

 

이제 enum을 만들었으니 써보자. SPRING 상수를 가져오면 봄이라는 문자열을 출력하게 만들고 싶다.

그러려면 계절 이름의 상수별로 한글 계절명들이 매핑돼 있어야 한다는 뜻인데, 이것 또한 간단하다. 아래와 같이 하면 된다. 클래스 내부에 선언한 enum에 작성했다.

 

public class Main {
    public static void main(String[] args) {
        System.out.println(Season.SPRING.getSeason());  // 봄
    }

    private enum Season {
        SPRING("봄"),
        SUMMER("여름"),
        FALL("가을"),
        WINTER("겨울");

        private String season;

        Season(String season) {
            this.season = season;
        }

        public String getSeason() {
            return season;
        }
    }

}

 

이렇게 하고 실행하면 봄이라는 한 글자가 출력되는 걸 볼 수 있다.

그런데 저렇게 하면 한 번에 하나의 글자만 가져올 수 있다. 저기 있는 모든 상수값들을 가져오고 싶다면 어떻게 해야 하나?

 

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        System.out.println(Arrays.toString(Season.values()));   // [SPRING, SUMMER, FALL, WINTER]
    }

    private enum Season {
        SPRING("봄"),
        SUMMER("여름"),
        FALL("가을"),
        WINTER("겨울");

        private String season;

        Season(String season) {
            this.season = season;
        }

        public String getSeason() {
            return season;
        }
    }

}

 

Season.values()를 감싸는 Arrays.toString()을 없애면 안 된다. 저게 있어야 오른쪽 주석에 사용한 대로 값들이 출력된다.

만약 Arrays.toString()이 없어지면 어떻게 될지 궁금하다면 직접 해보자. 그리고 왜 이렇게 되는지 생각해보는 것도 좋을 것 같다.

 

이펙티브 자바 3판에서도 enum에 대해 설명하고 있는데, 이 책에서 설명하는 내용은 아래와 같다.

 

열거 타입 자체는 클래스이며, 상수 하나당 자신의 인스턴스를 하나씩 만들어 public static final 필드로 공개한다
열거 타입은 밖에서 접근할 수 있는 생성자를 제공하지 않으므로 사실상 final이다. 따라서 클라이언트가 인스턴스를 직접 생성하거나 확장할 수 없으니 열거 타입 선언으로 만들어진 인스턴스들은 딱 하나씩만 존재함이 보장된다. 다시 말해 열거 타입은 인스턴스 통제된다. 싱글턴은 원소가 하나뿐인 열거 타입이라 할 수 있고, 열거 타입은 싱글턴을 일반화한 형태라고 볼 수 있다...(중략)

 

그리고 태양계를 모티브로 enum 클래스를 만들어 사용하는 예제와 사칙연산 이름들의 enum 클래스를 만들어 사용하는 예제를 보여주고 있다.

여기선 사칙연산 이름들의 enum 클래스 예제만 확인한다. 먼저 사칙연산을 정의한 enum 클래스를 생성한다.

 

public enum Operation {
    PLUS {
        public double apply(double x, double y) {
            return x + y;
        }
    },
    MINUS {
        public double apply(double x, double y) {
            return x - y;
        }
    },
    TIMES {
        public double apply(double x, double y) {
            return x * y;
        }
    },
    DIVIDE {
        public double apply(double x, double y) {
            return x / y;
        }
    };
    
    public abstract double apply(double x, double y);
}

 

이것은 추상 메서드를 만들어서 새 상수를 추가할 때 apply()를 추가하는 걸 잊지 않도록 유도한 예제다. 어차피 추상 메서드기 때문에 재정의하지 않으면 컴파일 오류로 알 수 있다.

위 코드를 조금 변형해서 toString()을 재정의해서 해당 연산을 의미하는 기호를 반환하게 하는 예제다.

 

public enum Operation {
    PLUS("+") {
        public double apply(double x, double y) {
            return x + y;
        }
    },
    MINUS("-") {
        public double apply(double x, double y) {
            return x - y;
        }
    },
    TIMES("*") {
        public double apply(double x, double y) {
            return x * y;
        }
    },
    DIVIDE("/") {
        public double apply(double x, double y) {
            return x / y;
        }
    };

    private final String symbol;
    
    Operation(String symbol) { this.symbol = symbol; }

    @Override
    public String toString() {
        return symbol;
    }
    
    public abstract double apply(double x, double y);
}

 

이걸 실제로 사용한다면 아래와 같다.

 

public class Main {
    public static void main(String[] args) {
        double x = Double.parseDouble("2");
        double y = Double.parseDouble("4");
        for (Operation op : Operation.values()) {
            System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y));
        }
    }
}

/*
2.000000 + 4.000000 = 6.000000
2.000000 - 4.000000 = -2.000000
2.000000 * 4.000000 = 8.000000
2.000000 / 4.000000 = 0.500000
*/

 

이외에도 enum에 대한 예제가 많이 있고, 인터넷에도 많이 있으니 참고해서 어떻게 쓰는지 익히면 좋을 것 같다.

그런데 enum은 단점이 없을까? 장점이 있는데 단점이 있을 리가 없다.

 

https://jeongmin.github.io/2017/10/08/android/android-enum-intdef/

 

코딩소년

이 글은 ENUM 사용 시 주의할 점과 어노테이션을 통해 이를 보완할 수 있는 방법에 대해 정리한 글입니다. 주로 참고한 자료는 유튜브 perfmatter 시리즈 중 The price of ENUMs (100 Days of Google Dev) 영상과 I

jeongmin.github.io

enum은 앱의 코드(DEX) 크기와 런타임 메모리 사용량을 증가한다. 앱의 DEX 크기가 증가한다는 건 APK 파일도 커지고 앱이 실행됐을 때 쓰는 메모리의 양도 그만큼 늘어난다는 걸 의미한다. 또한 enum은 Integer, String에 비해 더 많은 메모리를 런타임에 사용한다...(중략)...프로가드를 사용하면 enum을 Integer로 바꾸는 최적화를 수행해준다. 이걸 사용하거나 @IntDef 같은 TypeDef 어노테이션을 사용하면 된다.

 

위 링크에서 참고했다고 말한 자료는 아래와 같다.

 

https://www.youtube.com/watch?v=Hzs6OBcvNQE 

 

https://developer.android.com/studio/write/annotations.html

 

주석으로 코드 검사 개선  |  Android 개발자  |  Android Developers

주석을 사용하여 린트와 같은 코드 검사 도구에 힌트를 줌으로써 보다 미묘한 코드 문제를 더 잘 탐지하도록 할 수 있습니다.

developer.android.com

 

enum을 사용하면 확실히 편리하긴 하지만 남발해서 사용할 정도는 아니다. enum 자체가 갖는 비용도 무시할 수 없는 모양이니 꼭 필요한 경우가 아니라면, static final 상수로도 충분하다면 enum의 사용 여부는 고려해봐야 할 듯하다.

반응형
Comments