いまさらだけど Kotlin の value class を簡単に調べてみる

`value class`、コンパイラがいろいろと頑張るのだろうなぁと思いつつ、いままでどんな感じになるのか調べていなかったので、サクッと `javap` して雑に確認してみます。
※正確なところは仕様を追ったりしたほうが良いです。適当に試したメモみたいなものです。

とりあえず以下のような value class をつくって、
```kotlin:Foo.kt
@JvmInline
value class Foo(val value: String) {
    fun foo() = "value is $value"
}
```

コンパイル ＆ javap してみる。
```console
$ kotlinc Foo.kt
$ javap -p Foo
Compiled from "Foo.kt"
public final class Foo {
 private final java.lang.String value;
 public final java.lang.String getValue();
 public static final java.lang.String foo-impl(java.lang.String);
 public static java.lang.String toString-impl(java.lang.String);
 public java.lang.String toString();
 public static int hashCode-impl(java.lang.String);
 public int hashCode();
 public static boolean equals-impl(java.lang.String, java.lang.Object);
 public boolean equals(java.lang.Object);
 private Foo(java.lang.String);
 public static java.lang.String constructor-impl(java.lang.String);
 public static final Foo box-impl(java.lang.String);
 public final java.lang.String unbox-impl();
 public static final boolean equals-impl0(java.lang.String, java.lang.String);
}
```

さっくりみた感じ
- `value` をプロパティとしてもつ `Foo` クラスは作られる
- `Foo` のインスタンスを作ること無く処理できるように、引数に `value` の値を受け取るであろう `static` なメソッド(`-impl` の名称のもの)が生成される
  - コンストラクタ
  - `toString`, `hashCode`, `equals`
  - 定義したメソッド
- boxing / unboxing 用のメソッドが生成される

という感じ。
value class 使用箇所で、コンパイラが頑張って `-impl` なメソッド呼び出しのバイトコードを吐くのだろうなと推測がつく。

というわけで、試してみる

```kotlin:Bar.kt
interface Bar {
    fun bar(foo: Foo): String
}
```

value class をパラメータにとるメソッドのシグネチャはどうなるのか？
```java
$ javap -p Bar
Compiled from "Bar.kt"
public interface Bar {
  public abstract java.lang.String bar-GWb7d6U(java.lang.String);
}
```
メソッド名の後ろに `-GWb7d6U` という、あからさまにランダムな値(もしかしたらハッシュ値とかかも？)が付いたものとなる。
value class をパラメータに含めるとメソッド名が変形される模様。


コンストラクタだとどうなるのか気になったので試してみた
```kotlin:Baz.kt
class Baz(foo: Foo)
```
```java
$ javap -p Baz
Compiled from "Baz.kt"
public final class Baz {
  private Baz(java.lang.String);
  public Baz(java.lang.String, kotlin.jvm.internal.DefaultConstructorMarker);
}
```
パラメータが追加されたコンストラクタが作成され、パラメータが一つのものは `private` となっている。
(これ自体は、引数のデフォルト値のために使われていたりするものっぽい)


つづいて value class のメソッドを呼び出してみる。
```kotlin:Baz.kt
class Baz(foo: Foo) {
    init {
        println(foo.foo())
    }
}
```
```java
$ javap -c -p Baz
Compiled from "Baz.kt"
public final class Baz {
  private Baz(java.lang.String);
    Code:
       0: aload_0
       1: invokespecial #9                  // Method java/lang/Object."<init>":()V
       4: nop
       5: aload_1
       6: invokestatic  #15                 // Method Foo."foo-impl":(Ljava/lang/String;)Ljava/lang/String;
       9: getstatic     #21                 // Field java/lang/System.out:Ljava/io/PrintStream;
      12: swap
      13: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      16: nop
      17: return

  public Baz(java.lang.String, kotlin.jvm.internal.DefaultConstructorMarker);
    Code:
       0: aload_0
       1: aload_1
       2: invokespecial #34                 // Method "<init>":(Ljava/lang/String;)V
       5: return
}
```

予想通り `foo-impl` が呼び出されている (以下の部分)。
```java
       5: aload_1
       6: invokestatic  #15                 // Method Foo."foo-impl":(Ljava/lang/String;)Ljava/lang/String;
```

あと、インタフェースや型パラメータが使用されたことにより value class であるかどうか分からなくなる場合は、`box-impl` メソッドが使われます。まぁそりゃそうか、という感じ。
なので、`entities.map { it.foo } ...` みたいにして油断して list に入れてしまうと、`box-impl` が使われてインスタンスが生成されるので、いろいろと台無しになりそう。
このあたりは、もしかしたらそのうちに detekt とかで教えてくれるのかもしれないけど。


普通に使う分にはコンパイラが頑張ってくれているので、問題なく使えそうです。
一方でメソッド名が変形されたりするので、リフレクションでいろいろするライブラリと合わせて使うときは注意した方がよさそうです。