[IntelliJ] Kotlin to Java 디컴파일

kotlin 스펙에는 문제가 없으나 java 컴파일 과정에서 문제가 있는 경우가 있다. 예를 들면 제네릭의 경우 Type erasure 때문에 함수명 등이 겹칠 수 있다. 이런 경우 IntelliJ에서 디컴파일을 해보면 문제를 파악하기 쉽다.

fun List<Int>.avg(): Double = this.sum().toDouble() / this.size
fun List<Double>.avg(): Double = this.sum().toDouble() / this.size

fun main() {
    val l1 = listOf(1, 2, 3)
    val l2 = listOf(1.0, 2.0, 5.0)

    println(l1.avg())
    println(l2.avg())
}

예를 들어 위 코틀린 코드를 컴파일하면

Platform declaration clash: The following declarations have the same JVM signature (avg(Ljava/util/List;)D):

위처럼 자바에서 함수 시그니처가 겹친다는 에러가 나오게 된다.

import java.util.List;
import kotlin.Metadata;
import kotlin.collections.CollectionsKt;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 6, 0},
   k = 2,
   d1 = {"..."},
   d2 = {"main", "", "avg", "", "", "", "kotlin-temp"}
)
public final class MainKt {
   public static final double avg(@NotNull List param0) {
      // $FF: Couldn't be decompiled
   }

   public static final double avg(@NotNull List param0) {
      // $FF: Couldn't be decompiled
   }

   public static final void main() {
      List l1 = CollectionsKt.listOf(new Integer[]{1, 2, 3});
      List l2 = CollectionsKt.listOf(new Double[]{1.0, 2.0, 5.0});
      double var2 = avg(l1);
      System.out.println(var2);
      var2 = avg(l2);
      System.out.println(var2);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}

디컴파일을 해보면 이렇게 나오고, 왜 컴파일이 안되는지 직관적으로 알 수 있다

(위 예시의 경우엔, 함수에 @JvmName을 붙여야 한다)

IntelliJ Kotlin Bytecode 디컴파일 방법

Tools - Kotlin - Show Kotlin Bytecode 클릭

그럼 오른쪽에 이렇게 Kotlin Bytecode가 나오고, Decompile을 누르면 Java 버전의 코드로 변환된다.