4/2(土) Kotlin 1.0リリース記念勉強会 in 京都に行ってきた

Kotlinの勉強会にSpringBoot+Kotlinネタでしゃべりにいきました。 発表したスライドはこちらになります。

会場でAndroid以外でKotlinを使ってる人がどれくらい居るか質問したんですが、予想よりも多くて驚きました。 Androidアプリで「Java7までしか使えないからKotlin使うかー。使うしか無いかー。」みたいな使われ方するのがメインだと思ってたんですけど、普通にJavaの置き換えとして使うみたいな人が多いんですかね?

懇親会でJavaからKotlinを呼び出す時のnullの扱いについての話題が出ていたのでその辺を追加で書いてみようと思います。

とりあえず簡単なdataクラスを書きました。

data class User (
    var userId: String = "",
    var userName: String = "",
    var email: String? = null
)

コンパイル後のクラスファイルをJavaDecompilerで開いてみましょう

import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
 
@Metadata(mv={1, 1, 0}, bv={1, 0, 0}, k=1, d1={"\000\022\n\002\030\002\n\002\020\000\n\000\n\002\020\016\n\002\b\020\b?\b\030\0002\0020\001B%\022\b\b\002\020\002\032\0020\003\022\b\b\002\020\004\032\0020\003\022\n\b\002\020\005\032\004\030\0010\003��\006\002\020\006J\t\020\017\032\0020\003H?\003J\t\020\020\032\0020\003H?\003J\013\020\021\032\004\030\0010\003H?\003J)\020\022\032\0020\0002\b\b\002\020\002\032\0020\0032\b\b\002\020\004\032\0020\0032\n\b\002\020\005\032\004\030\0010\003H?\001R\034\020\005\032\004\030\0010\003X?\016��\006\016\n\000\032\004\b\007\020\b\"\004\b\t\020\nR\032\020\002\032\0020\003X?\016��\006\016\n\000\032\004\b\013\020\b\"\004\b\f\020\nR\032\020\004\032\0020\003X?\016��\006\016\n\000\032\004\b\r\020\b\"\004\b\016\020\n�N\006\023"}, d2={"Lnet/orekyuu/javatter/repos/User;", "", "userId", "", "userName", "email", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", "getEmail", "()Ljava/lang/String;", "setEmail", "(Ljava/lang/String;)V", "getUserId", "setUserId", "getUserName", "setUserName", "component1", "component2", "component3", "copy", "javabeamstudiopluginrepository_main"})
public final class User
{
  @NotNull
  private String userId;
  @NotNull
  private String userName;
  @Nullable
  private String email;
   
  public User(@NotNull String userId, @NotNull String userName, @Nullable String email)
  {
    this.userId = userId;this.userName = userName;this.email = email;
  }
   
  @NotNull
  public final String getUserId()
  {
    return this.userId;
  }
   
  public final void setUserId(@NotNull String <set-?>)
  {
    Intrinsics.checkParameterIsNotNull(<set-?>, "<set-?>");this.userId = <set-?>;
  }
   
  @NotNull
  public final String getUserName()
  {
    return this.userName;
  }
   
  public final void setUserName(@NotNull String <set-?>)
  {
    Intrinsics.checkParameterIsNotNull(<set-?>, "<set-?>");this.userName = <set-?>;
  }
   
  @Nullable
  public final String getEmail()
  {
    return this.email;
  }
   
  public final void setEmail(@Nullable String <set-?>)
  {
    this.email = <set-?>;
  }
   
  public User()
  {
    this(null, null, null, 7, null);
  }
   
  @NotNull
  public final String component1()
  {
    return this.userId;
  }
   
  @NotNull
  public final String component2()
  {
    return this.userName;
  }
   
  @Nullable
  public final String component3()
  {
    return this.email;
  }
   
  @NotNull
  public final User copy(@NotNull String userId, @NotNull String userName, @Nullable String email)
  {
    Intrinsics.checkParameterIsNotNull(userId, "userId");
    Intrinsics.checkParameterIsNotNull(userName, "userName");
    return new User(userId, userName, email);
  }
   
  public String toString()
  {
    return "User(userId=" + this.userId + ", userName=" + this.userName + ", email=" + this.email + ")";
  }
   
  /* Error */
  public int hashCode()
  {
    // Byte code:
    //   0: aload_0
    //   1: getfield 11 net/orekyuu/javatter/repos/User:userId  Ljava/lang/String;
    //   4: dup
    //   5: ifnull +9 -> 14
    //   8: invokevirtual 89    java/lang/Object:hashCode   ()I
    //   11: goto +5 -> 16
    //   14: pop
    //   15: iconst_0
    //   16: bipush 31
    //   18: imul
    //   19: aload_0
    //   20: getfield 27    net/orekyuu/javatter/repos/User:userName    Ljava/lang/String;
    //   23: dup
    //   24: ifnull +9 -> 33
    //   27: invokevirtual 89   java/lang/Object:hashCode   ()I
    //   30: goto +5 -> 35
    //   33: pop
    //   34: iconst_0
    //   35: iadd
    //   36: bipush 31
    //   38: imul
    //   39: aload_0
    //   40: getfield 33    net/orekyuu/javatter/repos/User:email   Ljava/lang/String;
    //   43: dup
    //   44: ifnull +9 -> 53
    //   47: invokevirtual 89   java/lang/Object:hashCode   ()I
    //   50: goto +5 -> 55
    //   53: pop
    //   54: iconst_0
    //   55: iadd
    //   56: ireturn
  }
   
  public boolean equals(Object paramObject)
  {
    if (this != paramObject)
    {
      if ((paramObject instanceof User))
      {
        User localUser = (User)paramObject;
        if ((!Intrinsics.areEqual(this.userId, localUser.userId)) || (!Intrinsics.areEqual(this.userName, localUser.userName)) || (!Intrinsics.areEqual(this.email, localUser.email))) {}
      }
    }
    else {
      return true;
    }
    return false;
  }
}

ところどころおかしなことになってますが、今回は気にしないことにします。 注目してもらいたいのが、フィールドやメソッドのアノテーション。なんと@NotNull・@Nullableが追加されてます! 何が嬉しいかというとKotlinで書かれたコードをJavaから呼び出す時、メソッドの戻り値でnullが返る可能性があるか分かりますし、IDEも警告を出してくれるのでNPEを出しにくく出来ます。 Kotlinで書かれたライブラリを使うことで、Javaコード側もKotlinのNull Safetyの恩恵を得られます。

とはいえここまではKotlinのライブラリをJavaから使うって話で、Javaで書かれたライブラリをKotlinで使う場合は、戻り値の型はNullableになりますし(Notnullアノテーションが書かれている場合は別)リフレクションで直接脳内フィールドにnullを入れられるとまずい感じはします。

というわけでおしまいです。 この辺はちゃんと調べられてないので間違っていたらTwitterとかでこっそり教えて下さい。