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とかでこっそり教えて下さい。