LoginSignup
2
3

More than 5 years have passed since last update.

Equalsを短く実装する

Posted at

はじめに

Eclipseを使用して実装している。hashCode()とかequals(Object)を生成してくるのだが、hashCode()はいいのだが、equals(Object)は長くて見にくい。いや、ちゃんと動くし、いいんですよ。しかし、個人的には生成されたソースが見た目悪いので、どうやればすっきりとしたソースになるか検証してみた。

基本のソース

EqualsTest.java
public class EqualsTest {

    private String name;
    private int age;
    private boolean isMan;
}

ベースのクラスはこんな感じ。オブジェクト、数値リテラル、boolean型を網羅するようにした。

hashCode()を見る

Eclipseが生成するコードは下記のよう。良いと思う。

EqualsTest.java
@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + age;
    result = prime * result + (isMan ? 1231 : 1237);
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    return result;
}

equals(Object)を生成する

EqualsTest.java
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    EqualsTest other = (EqualsTest) obj;
    if (age != other.age)
        return false;
    if (isMan != other.isMan)
        return false;
    if (name == null) {
        if (other.name != null)
            return false;
    } else if (!name.equals(other.name))
        return false;
    return true;
}

オブジェクト部分が長いと思う。一つのオブジェクトに5行だと、10つオブジェクト変数があると、50行になる。それは頂けないかと。

自分でequals(Object)を考えてみる

EqualsTest.java
public boolean myEquals(Object obj){
    if (this == obj) return true;
    if (obj == null || hashCode() != obj.hashCode() || ! (obj instanceof EqualsTest)) return false;

    EqualsTest other = (EqualsTest) obj;
    boolean result = true;
    result = result && (isMan == other.isMan);
    result = result && (age == other.age);
    result = result && (name == null ? other.name == null : name.equals(other.name));
        return result;
}

こんな感じでどうだろう。
変数がオブジェクトでも一行。だいぶ行数の節約になるはず。
生成されたソースはfalseとわかった時点でreturnしているので実行速度は速い。それに対し、こちらは変数分の判定を実施しているので遅い。しかし、個人的にはそのデメリット以上に見やすくて良いつもり。

getClassとinstanceOfの違い

さて二つの相違点として型比較を、生成されたものはgetClass()で、自分で考えてみたものはinstanceOfを使用しています。どこで差が出てくるかというと、継承を使ったときです。「Effective Java 第2版」の項目8から引用させて頂きます。
単純な座標情報のクラスPointがあったとします。Pointに色情報を追加したサブクラスColoredPointを作ります。さて、PointとColoredPointで座標が同じ場合どうなるでしょうか。
point.equals(coloredPoint)ではPointには色情報がないのだから、座標情報があればequalsはtrueでは?でもcoloredPoint.equals(point)は色情報の有無があるからfalseになるべきでは、ということになります。本来point.equals(coloredPoint)とcoloredPoint.equals(point)の結果は同じにならなければならないのに。
ちなみに本書のなかでは「継承よりコンポジションを選びなさい」というオチになってます。継承せずに、PointとColorの変数を持つColoredPointを作ろう、と。

終わりに

ソースは、
https://github.com/xaatw0/quiita/blob/master/src/EqualsTest.java
に置いてあります。生成されたequalsと自分で作成したequalsが同じ結果を出すことを確認したテストコード、継承を使ったときは違う結果になるテストコードも入っています。
それにしても、普通equalsの実装で皆さんどうされているのでしょうか。

2
3
1

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
2
3