これはKotlin Advent Calendar 2017 18日目の記事です(投稿が遅くなってスミマセン )
Javaエンジニア必読のEffective Javaの観点でKotlinを眺めてみると、Kotlinはとても気が利いていることがよくわかります。
ここではEffective Java第3章の項目8、項目9のequals
とhashCode
のOverrideをピックアップして見てみます。
equals
とhashCode
のOverride
概要
Effective Java第3章の項目8、項目9をおさらいしてみます。
ざっと以下のようなことが書かれています。
- クラスに論理的等価性の概念を持たせたい場合、
equals
をoverrideします。 -
equals
メソッドは以下の一般契約を満たす必要がある- 反射的, 対照的, 推移的, 整合的, non nullな
x
に対してx.equals(null)
はfalse
※ 詳細を知りたい方はググると情報が出てきますので探してみて下さい
- 反射的, 対照的, 推移的, 整合的, non nullな
-
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;
}
}
ちなみにequals
とhashCode
はIntelliJに自動生成してもらいました。
さて、このあとクラスにfieldが追加されるなどの変更があった場合、このequals
とhashCode
は当初想定したような仕様を満たし続けることができるのでしょうか。
それは実装する人に委ねられていて、意識して守っていかなければならない部分になってしまいますね
Kotlinで実装してみる
これが、Kotlinだとどうなるのでしょうか。
data class DataB(
val a: String,
val b: String,
val c: String?
)
ご存知data class
を使えばOK
これを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;
}
}
}
ちゃんとequals
とhashCode
ができていますね。
そしてJavaの時の自動生成コードと同じように振る舞いそう。。。多分。(確認してない
(´-`).。oO(hashCode、NonNullのfieldもnullチェックしてるんだなぁ)
このように、Kotlinのdata class
はequals
とhashCode
を自動で実装してくれるおかげで、クラスに変更を加えても当初の仕様を継続的に守っていきやすそうですね。
ちなみに
equals
とhashCode
のOverrideはLombokを使ってもいい感じにしてくれます
(Lombokの機能とKotlinを比べてみても面白いかも?)
終わりに
今回はequals
とhashCode
だけでしたが、KotlinにはEffectiveJavaを実践するうえでとても便利な機能がまだまだあります。
結構気の利いたことしてくれてることが理解できるのでEffectiveJavaと比較してKotlinの言語機能を眺めてみるのも面白いかもしれません。
ちなみにEffectiveJavaは結構前に出版されて内容が古い部分もいくらかあるのですが、もうすぐ第3版が発売されるので興味のある方は是非!
https://www.amazon.co.jp/Effective-Java-3rd-Joshua-Bloch/dp/0134685997
参考