일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 플러터 설치 2022
- 안드로이드 레트로핏 사용법
- 스택 자바 코드
- ar vr 차이
- 클래스
- ANR이란
- 안드로이드 os 구조
- jvm 작동 원리
- 스택 큐 차이
- 안드로이드 유닛 테스트
- android ar 개발
- Rxjava Observable
- 서비스 vs 쓰레드
- 안드로이드 유닛 테스트 예시
- 2022 플러터 안드로이드 스튜디오
- rxjava cold observable
- 안드로이드 레트로핏 crud
- 안드로이드 라이선스 종류
- 객체
- rxjava disposable
- jvm이란
- 서비스 쓰레드 차이
- 자바 다형성
- 멤버변수
- android retrofit login
- 안드로이드 라이선스
- 2022 플러터 설치
- 큐 자바 코드
- rxjava hot observable
- 안드로이드 유닛테스트란
- Today
- Total
나만을 위한 블로그
[JAVA] enum(열거형)이란? 본문
안드로이드 공부를 하다보면 깃허브에서 예제 프로젝트를 찾아보기 마련인데, 그러다 보면 가끔씩 이런 형태의 코드를 볼 수 있다.
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
열거형, 이넘(enum)은 요소, 멤버라 불리는 명명된 값의 집합을 이루는 자료형이다. 열거자 이름들은 일반적으로 해당 언어의 상수 역할을 하는 식별자다. 일부 열거자 자료형은 언어에 기본 소속되어 있을 수 있다.
http://tutorials.jenkov.com/java/enums.html
자바 Enum은 상수 컬렉션을 정의하는 데 쓰이는 특수한 자바 유형(type)이다. 더 정확하게 말하면 enum은 특수한 자바 클래스다. 열거형에는 상수, 메서드 등이 포함될 수 있으며 자바 5부터 추가되었다
https://www.w3schools.com/java/java_enums.asp
enum은 상수 그룹(final 변수처럼 바꿀 수 없는 변수)을 나타내는 특수한 "클래스"다. 열거형을 만들려면 class, interface 대신 enum 키워드를 사용하고 상수를 쉼표로 구분한다. 또한 대문자여야 한다.
대문자로 써서 쉼표로 구분하고 enum 키워드를 붙이면 열거형이 완성되는 것 같다.
그런데 이 enum은 언제 쓰는 것인가? 무슨 문제가 있어서 enum이란 개념이 생긴 것인가? 그리고 이걸 써서 얻는 장점은 뭐가 있을까?
장점
1. 코드의 양이 줄었다
2. 인스턴스 생성과 상속 시도 시 컴파일 에러
3. enum이란 키워드로 열거형의 의도를 명확히 드러냄
2번째 장점은 중간에 말이 끊겨서 뭘 말하고자 하는지 모르겠다. 일단 컴파일 에러를 없앤다는 의미로 생각하고 넘긴다.
https://techblog.woowahan.com/2527/
개발을 진행할 때 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
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/
어떤 종류의 숫자 또는 텍스트 데이터를 나타내는 미리 정의된 값 목록이 필요한 경우 열거형을 사용해야 한다. 예를 들어 체스 게임에서 다양한 유형의 말을 열거형으로 나타낼 수 있다. 변수(특히 메서드 매개변수)가 가능한 값의 작은 집합에서 하나만 가져올 수 있는 경우 항상 열거형을 써야 한다. 정수(또는 문자열 코드) 대신 열거형을 사용하는 경우, 컴파일 시간 검사를 늘리고 잘못된 상수를 전달하는 오류를 방지하고 어떤 값을 사용할 수 있는지 문서화한다
그럼 이 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은 앱의 코드(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
enum을 사용하면 확실히 편리하긴 하지만 남발해서 사용할 정도는 아니다. enum 자체가 갖는 비용도 무시할 수 없는 모양이니 꼭 필요한 경우가 아니라면, static final 상수로도 충분하다면 enum의 사용 여부는 고려해봐야 할 듯하다.
'JAVA' 카테고리의 다른 글
[JAVA] 소켓 프로그래밍이란? 클라이언트/서버 채팅 예제 (0) | 2022.03.06 |
---|---|
[JAVA] 인텔리제이에서 JUnit 환경설정하기 (0) | 2022.01.31 |
[JAVA] JVM의 작동원리 (0) | 2021.10.08 |
[JAVA] 버퍼란? BufferedReader/Writer란? + 예제 (0) | 2021.09.04 |
[JAVA] SOLID 원칙이란? - 2 - (0) | 2021.07.24 |