• JvmName과 IntelliJ IDEA의 함수 검색, 수정 기능 관련 테스트
    언어/Kotlin 2025. 3. 16. 20:02
    반응형

    코틀린을 사용하게되면, 확장함수를 사용하고 싶어진다. 코드가 코틀린스럽고, 깔끔해지기 때문이다.

    간단하게, Int 타입의 List를 받아서 각 항목이 Map의 key, value가 되는 코드를 작성해보자.

     

     

    1. 함수 사용

    fun main() {
        val intList: List<Int> = listOf(1, 2, 3, 4)
        val map: Map<Int, Int> = getKeyValueMapFrom(intList)
    
        map.forEach { println(it) }
    }
    
    private fun getKeyValueMapFrom(list: List<Int>): Map<Int, Int> = list.associateWith { it }

     

     

     

    2. 확장 함수 사용

    fun main() {
        val map: Map<Int, Int> = listOf(1, 2, 3, 4).toKeyValueMap()
    
        map.forEach { println(it) }
    }
    
    private fun List<Int>.toKeyValueMap(): Map<Int, Int> = this.associateWith { it }

     

    결과는 동일하지만 확장함수를 사용해 코드가 조금 더 깔끔해졌다. 하지만, 확장 함수가 추가되면 문제가 발생한다.

    String 타입의 List를 받아서 각 항목이 Map의 key, value가 되는 코드를 추가로 작성해보자.

     

     

     

    3. 확장 함수 추가

    fun main() {
        val map1: Map<Int, Int> = listOf(1, 2, 3, 4).toKeyValueMap()
        map1.forEach { println(it) }
    
        val map2: Map<String, String> = listOf("1", "2", "3", "4").toKeyValueMap()
        map2.forEach { println(it) }
    }
    
    private fun List<Int>.toKeyValueMap(): Map<Int, Int> = this.associateWith { it }
    private fun List<String>.toKeyValueMap(): Map<String, String> = this.associateWith { it }

    위 코드는 빌드 시점에 오류가 발생한다.

    타입 이레이저로 List<Int>와 List<String>은 동일한 List로 컴파일되기 때문이다.

     

    결과적으로 바이트코드에는 List.toKeyValueMap(): Map<Int, Int>, List.toKeyValueMap(): Map<String, String>이 존재하게 된다. 확장함수 명이 동일하므로, 오류가 발생한다.

     

     

     

    4. JVMName 추가

    JVMName 어노테이션을 추가하면 바이트코드의 명칭을 바꿔주기 때문에, toKeyValueMap이라는 함수명을 사용할 수 있다.

    fun main() {
        println("map1::")
        val map1: Map<Int, Int> = listOf(1, 2, 3, 4).toKeyValueMap()
        map1.forEach { println(it) }
    
        println("\nmap2::")
        val map2: Map<String, String> = listOf("1", "2", "3", "4").toKeyValueMap()
        map2.forEach { println(it) }
    }
    
    @JvmName("intListToKeyValueMap")
    private fun List<Int>.toKeyValueMap(): Map<Int, Int> = this.associateWith { it }
    
    @JvmName("stringListToKeyValueMap")
    private fun List<String>.toKeyValueMap(): Map<String, String> = this.associateWith { it }

     

     

     

     

     

    패키지를 새로 만들어 private 함수를 public으로 변경하고 여러군데서 import할 수 있도록 변경해보자. 그리고, Main 클래스를 여러개 만들어보자. 패키지 구조는 다음과 같다.

     

     

    // ListExtension.kt
    package org.example.extension
    
    @JvmName("intListToKeyValueMap")
    fun List<Int>.toKeyValueMap(): Map<String, String> = this.associate { it.toString() to it.toString() }
    
    @JvmName("stringListToKeyValueMap")
    fun List<String>.toKeyValueMap(): Map<String, String> = this.associateWith { it }

     

    Main1, Main2, Main3이 동일하고, Main4, 5가 동일하다.

    // Main1~3
    package org.example
    
    import org.example.extension.toKeyValueMap
    
    fun main() {
        println("map1::")
        val map1: Map<String, String> = listOf(1, 2, 3, 4).toKeyValueMap()
        map1.forEach { println(it) }
    }
    // Main4~5
    package org.example
    
    import org.example.extension.toKeyValueMap
    
    fun main() {
        println("map2::")
        val map2: Map<String, String> = listOf("1", "2", "3", "4").toKeyValueMap()
        map2.forEach { println(it) }
    }

     

     

     

     

    5. IDE의 기능 - 함수 사용위치 탐색, 수정에 대하여

    IntelliJ IDEA의 기능으로 command+클릭(맥북기준)은 함수가 사용중인 위치를 탐색해준다.

     

    분명 Main1, Main2, Main3은 List<Int>를 사용하고, Main4, Main5는 List<String>을 사용하고 있다. List<String>이 사용되는 부분은 Main4, Main5로 나오지만, import문은 전체 다 검색되는 것을 알 수 있다.

     

    만약, List<String>.toKeyValueMap()을 수정한다면? List<String>을 사용하는 부분은 정확히 검색이 되지만, import되는 부분은 동일 한 이름의 함수를 모두 검색한다.

     

    또한, IntelliJ의 또 다른 기능인 "이름변경"을 통해 수정한다면? 사용되는 부분만 변경을 해주는지? 모든 import까지 변경해줄지 기능에 대한 혼동이 생긴다.

     

     

    toKeyValueMap2로 변경해보자. 변경 시 사용되는 부분의 import와 함수명만 바뀐것을 확인할 수 있다.

     

     

    결론, JvmName과 IntelliJ IDEA의 함수 검색과 수정 기능에 대한 테스트를 해봤다.

    반응형

    댓글

Designed by Tistory.