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

これは[Kotlin Advent Calendar 2017](https://qiita.com/advent-calendar/2017/kotlin) 18日目の記事です（投稿が遅くなってスミマセン :bow:)


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

ここではEffective Java第３章の項目8、項目9の`equals`と`hashCode`のOverrideをピックアップして見てみます。

# `equals`と`hashCode`のOverride

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

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

## Javaで実装してみる

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;
    }
}

```

ちなみに`equals`と`hashCode`はIntelliJに自動生成してもらいました。

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

## Kotlinで実装してみる

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

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

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

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

```java
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;
      }
   }
}


```

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


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

# ちなみに

`equals`と`hashCode`のOverrideは[Lombok](https://projectlombok.org/)を使ってもいい感じにしてくれます :sweat_smile: 
(Lombokの機能とKotlinを比べてみても面白いかも？)

# 終わりに

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

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

# 参考
https://www.amazon.co.jp/EFFECTIVE-JAVA-Java-Joshua-Bloch/dp/4621066056/ref=pd_lpo_sbs_14_t_0?_encoding=UTF8&psc=1&refRID=VF4RXTQQV4QXR9KD78S0