티스토리 뷰

반응형

이전부터 문제 많던 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 같은 한 번만 실행되는 곳에서 수행한다면 크게 문제가 되지 않을 수 있다.

또한, 서비스 코드 단의 실행이 잦은 곳이 아니라면 그렇게 크게 문제가 되지 않을 수 있다.

 

문제는 라이브러리/프레임워크다.

내부적으로 얽혀있어 요청 당 많이 호출될 가능성도 있고, 숨겨있어서 성능 병목현상이 발생하는지 여부도 파악하기 어렵다.

 

이 이슈와 관련되어 다양한 프레임워크에서 수정이 이루어졌었다.

 

 

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를 도입하려면 먼 것으로 보이지만, 그래도 계속해 발전해나가는 언어의 모습을 보니 재밌으면서, “한 번 설계 잘못하면 이렇게 고생합니다”를 한 번 더 깨닫는다.

320x100
반응형
댓글
반응형
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함