LoginSignup
6
4

More than 5 years have passed since last update.

Effective Javaの観点からKotlinを見てみる

Last updated at Posted at 2017-12-23

これはKotlin Advent Calendar 2017 18日目の記事です(投稿が遅くなってスミマセン :bow:)

Javaエンジニア必読のEffective Javaの観点でKotlinを眺めてみると、Kotlinはとても気が利いていることがよくわかります。

ここではEffective Java第3章の項目8、項目9のequalshashCodeのOverrideをピックアップして見てみます。

equalshashCodeのOverride

概要

Effective Java第3章の項目8、項目9をおさらいしてみます。
ざっと以下のようなことが書かれています。

  1. クラスに論理的等価性の概念を持たせたい場合、equalsをoverrideします。
  2. equalsメソッドは以下の一般契約を満たす必要がある
    • 反射的, 対照的, 推移的, 整合的, non nullなxに対してx.equals(null)false ※ 詳細を知りたい方はググると情報が出てきますので探してみて下さい :bow:
  3. equalsをOverrideした場合、hashCodeもOverrideする必要がある

Javaで実装してみる

Javaで実装してみると以下のように実装してみました。

public class DataA {
    @NotNull
    private final String a;
    @NotNull
    private final String b;
    @Nullable
    private final String c;

    public DataA(@NotNull String a, @NotNull String b, @Nullable String c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        DataA dataA = (DataA) o;

        if (!a.equals(dataA.a)) return false;
        if (!b.equals(dataA.b)) return false;
        return c != null ? c.equals(dataA.c) : dataA.c == null;
    }

    @Override
    public int hashCode() {
        int result = a.hashCode();
        result = 31 * result + b.hashCode();
        result = 31 * result + (c != null ? c.hashCode() : 0);
        return result;
    }
}

ちなみにequalshashCodeはIntelliJに自動生成してもらいました。

さて、このあとクラスにfieldが追加されるなどの変更があった場合、このequalshashCodeは当初想定したような仕様を満たし続けることができるのでしょうか。
それは実装する人に委ねられていて、意識して守っていかなければならない部分になってしまいますね :cry:

Kotlinで実装してみる

これが、Kotlinだとどうなるのでしょうか。

data class DataB(
        val a: String,
        val b: String,
        val c: String?
)

ご存知data classを使えばOK :laughing:

これをDecompileしてみましょう。

public final class DataB {
   @NotNull
   private final String a;
   @NotNull
   private final String b;
   @Nullable
   private final String c;

   ...

   public int hashCode() {
      return ((this.a != null ? this.a.hashCode() : 0) * 31 + (this.b != null ? this.b.hashCode() : 0)) * 31 + (this.c != null ? this.c.hashCode() : 0);
   }

   public boolean equals(Object var1) {
      if (this != var1) {
         if (var1 instanceof DataB) {
            DataB var2 = (DataB)var1;
            if (Intrinsics.areEqual(this.a, var2.a) && Intrinsics.areEqual(this.b, var2.b) && Intrinsics.areEqual(this.c, var2.c)) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }
   }
}


ちゃんとequalshashCodeができていますね。
そしてJavaの時の自動生成コードと同じように振る舞いそう。。。多分。(確認してない
(´-`).。oO(hashCode、NonNullのfieldもnullチェックしてるんだなぁ)

このように、Kotlinのdata classequalshashCodeを自動で実装してくれるおかげで、クラスに変更を加えても当初の仕様を継続的に守っていきやすそうですね。

ちなみに

equalshashCodeのOverrideはLombokを使ってもいい感じにしてくれます :sweat_smile:
(Lombokの機能とKotlinを比べてみても面白いかも?)

終わりに

今回はequalshashCodeだけでしたが、KotlinにはEffectiveJavaを実践するうえでとても便利な機能がまだまだあります。
結構気の利いたことしてくれてることが理解できるのでEffectiveJavaと比較してKotlinの言語機能を眺めてみるのも面白いかもしれません。

ちなみにEffectiveJavaは結構前に出版されて内容が古い部分もいくらかあるのですが、もうすぐ第3版が発売されるので興味のある方は是非!
https://www.amazon.co.jp/Effective-Java-3rd-Joshua-Bloch/dp/0134685997

参考

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4