JVM signature error 대처하기

Kim, Sujin·2023년 2월 27일
1

우아한테크코스

목록 보기
3/3
post-thumbnail
class Lotto(val lotto: List<LottoNumber>

다음과 같이 LottoNumber 리스트를 프로퍼티로 가지고 있는 Lotto 클래스가 있다.

참고로 LottoNumber 객체는 로또 범위 내의 숫자들을 미리 인스턴스화 해두었고 팩토리 함수를 통해 이를 리턴하는 형태이다.

이 경우, Lotto 객체를 생성하려면 다음과 같이 코드가 작성된다.

val lotto: Lotto = Lotto(listOf(1,2,3,4,5,6).map { LottoNumber.create(it) })

매번 생성할 때마다 다음과 같이 코드를 작성한다면
이는 일반 코드 뿐만 아니라 테스트 코드에서도 엄청난 가독성 방해(!?!)가 일어나게 될 것이고,
좀 더 간단하게 바꿔주고 싶은 강한 욕구가 든다.


constructor(numbers: List<Int>) : this(numbers.map { LottoNumber.create(it) })

이를 해결하기 위해 위와 같이 부생성자를 만들게 된다면 어떻게 될까?

Platform declaration clash: The following declarations have the same JVM signature ((Ljava/util/List;)V):
constructor Lotto(numbers: List) defined in lotto.model.Lotto
constructor Lotto(lotto: List) defined in lotto.model.Lotto

그렇다.. 다음과 같은 오류를 마주하게 된다. :(


이 오류가 무슨 의미냐면,
List< Int >와 List< LottoNumber >가 같은 JVM signature를 갖고있다는 의미다.
즉, 이 시그니처가 동일하기 때문에 코틀린 코드가 바이트코드로 변경될 때 오류가 발생하는 것이다.

이는 JVM compiler가 파라미터 타입으로 함수 또는 생성자를 구분할 때 generic type은 구분하지 못하기 때문이다.
List< Int >, List< String >, List< LottoNumber > 까지 <> 안에 무엇이 들어와도 에러가 발생한다.


이와 같이 JVM signature 오류가 발생한 경우, 보통 함수 앞에 @JvmName를 사용함으로써 해결할 수 있다.
@JvmName은 JVM signature를 변경할 때 사용하는 annotation이다.

// 예시
@JvmName("callFromString")
fun call(a: List<String> {}
@JvmName("callFromInt")
fun call(a: List<Int>) {}

하지만 @JvmName은 생성자에서는 사용할 수 없다.
이 경우, 이를 해결하기 위한 방법은 무엇이 있을까 ???

vararg

constructor(vararg numbers: Int): this(numbers.map { LottoNumber.create(it) })

list< Int > 대신 가변인자 vararg 를 사용할 수 있다.
생성할 때 int array를 넘기면 되는데, 이 때 배열 앞에 * 표시를 붙여주어야 한다.

팩토리 함수

companion object {
    fun create(lotto: List<Int>) = Lotto(lotto.map { LottoNumber.create(it) }.sortedBy { it.toInt() })
}

companion object 객체를 활용하여 팩토리 함수를 정의한다.

Dummy Implicit

constructor(
    numbers: List<Int>,
    @Suppress("UNUSED_PARAMETER") dummyImplicit: Any? = null
): this(numbers.map { LottoNumber.create(it) })

사용하지 않는 매개변수를 하나 추가하여 다른 시그니처가 생성되도록 한다.


더 많은 방법이 있을 수도 있겠지만 우선 위의 3가지 방법을 공부해보았다.
물론 무엇이 더 옳은지 나쁜지 정답은 없기에, 상황에 맞게 편한 방법으로 대처하면 될 것 같다 :)


참고 || 참고 할 만한 자료
링크
Kotlin annotation 정리
팩토리 함수

0개의 댓글