Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

これは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

参考

atsuya046
フリーのエンジニア Android, Webアプリケーション開発など
http://atsuya046.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away