티스토리 뷰
[Kotlin] Enum.values() 사용하지 말자, 이제
KimDoubleB 2023. 8. 22. 01:54이전부터 문제 많던 Enum의 values()
.
Kotlin v1.9.0에서는 드디어 Enum.values()
를 대체하는 녀석인 entries
property를 stable feature로 제공한다.
내부적인 구현은 다르지만, 사용하는 법은 values()
와 같다고 보면 된다.
fun main() {
val americaBrand = Brand.entries.filter { it.country == "America" }
println(americaBrand)
}
enum class Brand(val country: String) {
SAMSUNG("Korea"),
APPLE("America"),
GOOGLE("America"),
MICROSOFT("America"),
}
릴리즈 문서에서는 이제 아예 values()
대신 entries
사용을 권장하고 있다.
The values() function is still supported, but we recommend that you use the entries property instead.
https://kotlinlang.org/docs/whatsnew19.html#stable-replacement-of-the-enum-class-values-function
구현으로는 EnumEntries<E>
type을 만들어 제공한다.
sealed interface EnumEntries<E : Enum<E>> : List<E>
- 클래스 명으로 값에 대한 의미를 나타낼 수 있고, 불변형을 나타내고자 새로운 인터페이스인
EnumEntries
를 만들어 제공한다.
왜 values()는 미움받는가?
일단, Java 내에서 values()
는 "API design bug"로 여겨진다.
여러가지 이유가 있다.
1. 호출할 때마다 새로운 Array instance를 만든다.
values()
를 사용하는 사람들의 착각이 제일 큰 부분인데, 내부에서 static으로 만들어두고 활용하는 것이 아니다.
호출할 때마다 열거형 항목들을 새로운 Array에 담아 반환한다. 그렇기에 아래 테스트는 통과한다.
@Test
fun enumTest() {
val brands1 = Brand.values()
val brands2 = Brand.values()
println(brands1) // @478ee483
println(brands2) // @1a7288a3
assertThat(brands1 == brands2).isFalse()
}
Enum 열거형 데이터는 동적으로 추가되지 않는다. 그렇기에 이는 비효율적인 것이다.
이러한 values()
를 한 두군데, 그것도 static 같은 한 번만 실행되는 곳에서 수행한다면 크게 문제가 되지 않을 수 있다.
또한, 서비스 코드 단의 실행이 잦은 곳이 아니라면 그렇게 크게 문제가 되지 않을 수 있다.
문제는 라이브러리/프레임워크다.
내부적으로 얽혀있어 요청 당 많이 호출될 가능성도 있고, 숨겨있어서 성능 병목현상이 발생하는지 여부도 파악하기 어렵다.
이 이슈와 관련되어 다양한 프레임워크에서 수정이 이루어졌었다.
- spring -
HttpStatus.values()
- mssql-jdbc
- kotlin-serialization
2. Collection이 아닌 변경 가능한(Mutable) array를 반환한다.
활용도 높은 Collection이 아닌 배열(array)을 반환하고, 그렇다보니 Mutable 하다.
val brands: Array<Brand> = Brand.values()
불변이 아니기에 악의적인 개발자가 중간에 배열의 값을 조작할 수 있다.
- 당연하게도 가벼운 로직에서는 보기 쉬울 수는 있으나, 복잡한 로직에서는 파악하기 어려울 수 있다.
또한, 이를 Collection으로 활용하기 위해서는 꼭 변환이 필요하다.
그래서 values()
를 사용하는 곳에서는 아래와 같은 코드를 쉽게 찾아볼 수 있다.
Brand.values().toList().stream()
.filter { it.country == "America" }
Brand.values().toMutableList().stream()
.filter { it.country == "America" }
Java라면 한 번 감싸줘야한다. (불편하다)
Arrays.stream(Brand.values())
.filter(brand -> brand.getCountry().equals("America"))
.toList();
이러한 이슈들로 인해 Enum.values()
는 사용할 때 주의해야 하며, Kotlin에서는 이를 대체하기 위한 entries
가 나온 것이다.
변경해나가는 방법
큰 오픈소스에서는 이러한 새로운 제안들을 문서로 관리하는데, Kotlin에서는 KEEP
을 통해 관리한다.
여기서 entries에 대한 내용을 보면, 어떻게 Enum.values()
를 폐기할지에 대한 내용이 나온다.
Enum.values() 는 2004년에 릴리즈 된 Java 1.5 부터 사용되어 왔고, Kotlin 초기부터 사용되어져왔기에 이를 바로 사용 못하게 하는 것은 큰 혼란을 불러 일으킬 수 있다.
그러므로 IDE의 도움을 받아 부드럽게 폐기(will be softly decommissioned)해나가겠다.
-- values() 사용에 대한 우선순위를 낮추고, IDE 자동완성에서 제거한다.
-- values() 사용을 entries 로 대체하도록 가이드하는 Soft warning을 추가한다.
-- Kotlin 가이드 등의 문서에서 entries 를 사용하도록 업데이트한다.
폐기에 있어 IDE를 사용해나간다는게 참 신기했다.
- 현재 사용하는 IDE(Intellij IDEA)에서도 오류표시라던지, 가이드를 통해 특정 기능들은 제한을 하거나 하는데 이게 이런 목적이었구나 깨달았다.
- 이러한 것(우선순위, 대체대상 등)들을 관리하는 문서 또는 형식이 있는지는 찾아봐야겠다. (IDE 별로 관리하면 너무 힘들 것 같은데, 개발 툴 킷에 포함되려나...)
정리
Enum.values()
사용 시 주의해야 하는 이유와 Kotlin 1.9 이후에서 이를 대체할 entries
를 알아보았다.
역시나 큰 오픈소스 프로젝트들은 탄탄한 계획/제안 문서를 바탕으로 기능들이 이루어지고 있구나 깨닫기도 했고, 새로운 기능을 도입할 때 IDE를 이용한 방식으로 제안된다는 것도 참 신기했다.
사실 아직 Kotlin 1.9를 도입하려면 먼 것으로 보이지만, 그래도 계속해 발전해나가는 언어의 모습을 보니 재밌으면서, “한 번 설계 잘못하면 이렇게 고생합니다”를 한 번 더 깨닫는다.
'Development > Java, Kotlin, Frameworks' 카테고리의 다른 글
헥사고날 아키텍처를 구현하는 여러가지 방법 (2): 멀티모듈, JPMS 그리고 Kotlin (1) | 2024.06.07 |
---|---|
헥사고날 아키텍처를 구현하는 여러가지 방법 (1): Java 접근제한자와 ArchUnit (1) | 2024.06.06 |
Serverless - Spring (with GraalVM Native) (1) | 2023.04.17 |
Spring boot - API Versioning (0) | 2023.04.16 |
[springdoc-openapi / Swagger] Failed to load remote configuration 이슈 해결 (1) | 2023.01.11 |
- Total
- Today
- Yesterday
- Intellij
- OpenTelemetry
- container
- Spring
- k8s
- c++
- Spring boot
- boj
- java
- docker
- 로그
- WebFlux
- 클린 아키텍처
- python
- MySQL
- 하루
- 쿠버네티스
- jasync
- 일상
- Algorithm
- 알고리즘
- Log
- HTTP
- gradle
- tag
- 비동기
- Clean Architecture
- Istio
- 백준
- Kubernetes
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |