この記事の概要
同一性と、同値性について具体的なコードで動作を確認します。
【キーポイント】
・ ==演算子
・equalsメソッド、そのオーバーライド
・独自オブジェクトの同一性、同値性の判定
・String、Integerの特殊な動作について(定数化=コンスタントプールに格納)
同一性と、同値性とは
表でまとめると以下になります。
同一性 | 同値性 | |
意味 | 同じインスタンスということ ex)aとbは別インスタンス Item a = new Item (); Item b = new Item (); ex) aとcは同一インスタンス Item a = new Item (); Item c = a; | 別インスタンスだけど内容が同じ ex) Item はidとnameのフィールドを持っている a.id 10 a.name Windows b.id 10 b.name Windows |
判定方法 | ==演算子 | equalsメソッド |
検証内容と検証結果
■クラスの概要
クラス名 | 説明 |
Item | idとnameをフィールドにもつクラス (検証1)~(検証4)の実装で使用しているクラス |
Test | (検証1)~(検証6)コードを実装 |
■検証内容と結果整理
NO | ケース | コード抜粋 | 結果 |
検証1 | 別オブジェクトで同一値 | Item a = new Item(2000,”tokyo”); Item b = new Item(2000,”tokyo”); | a==bの結果: false a.equals(b)の結果: true |
検証2 | 同一オブジェクトへの参照 | Item c = a; String str2 = String.valueOf( a==c ); | a==cの結果: true a.equals(c)の結果: true |
検証3 | 別オブジェクトで非同一値 | Item a = new Item(2000,”tokyo”); Item d = new Item (1000,”tiba”); | a==dの結果: false a.equals(d)の結果: false |
検証4 | 各オブジェクトのハッシュコード | a.hashCode() b.hashCode() c.hashCode() d.hashCode() | aのハッシュコード: 110604887 bのハッシュコード: 110604887 cのハッシュコード: 110604887 dのハッシュコード: 3591757 |
検証5 | newなしString | String e =(“hello”); String f =(“hello”); String g = new String(“Hello”); String h = new String(“Hello”); | ■newなし e==fの結果: true e.equals(f)の結果: true ■newあり g==hの結果: false g.equals(h)の結果: true |
検証6 | newなしInteger | Integer i = Integer.valueOf(1); Integer ii = Integer.valueOf(1); Integer j = Integer.valueOf(127); Integer jj = Integer.valueOf(127); Integer k = Integer.valueOf(128); Integer kk = Integer.valueOf(128); | i==ii(1)の結果: true j==jj(127)の結果: true k==kk(128)の結果: false |
実装
■Itemクラス
絵の吹き出しの通りです。「id」、「name」のフィールド部分のみ手動で作成
あとは、右クリック>ソース>で対象のジェネレータを選択し作成しています。
以下を使用しています。
・フィールドを使用してコンストラクター生成
・getterおよびsetterの生成
・hashCode()およびequals()の生成
【ポイント】
hashCode()およびequals()の生成
Itemクラスの比較をequalsで実施する場合は、このように、オーバライドして使用する必要
があります。
以下に示す、参考1の通りオーバライドしないとオブジェクトの参照を比較しているだけ
だからです。これだと「別インスタンスだけど内容が同じ」場合にtrueが返却されません。
オーバライドすることでtrueを返却できるようになります。
【参考1】
Objectクラスは、すべてのクラスのルートクラスでそのメソッドにequalsも定義されています。
ここで定義されている内容は、以下に示す通り、オブジェクトの参照を比較しているだけです。
■Testクラス
<検証1~検証3>
結果は前述の「検証内容と結果整理」に示した通りで違和感ないと思います。
インスタンスが同一なのか、インスタンスが別で値が同じなのか、値も違うのかを考えれば
納得できると思います。
<検証4>
ハッシュコードとは、内容が同じなら同じコード値になります。
ItemクラスでhashCode()でオーバライドしていますが、idとnameの内容を使用して
値を導いています。よって、idとnameがどちらも同じなら、この値は同じになります。
上記を踏まえると、a,b,cが同一でdだけ値が違うのも納得できると思います。
<検証5>
String型をnewした場合は、「g==hの結果」が「false」で想定通りです。インスタンスが別なので、
同一でないため、falseです。
しかし、「e==fの結果」が「true」になります。これは違和感があります。別インスタンスじゃないの?となりますが、この場合定数扱いで処理され「コンスタントプール」という領域に定数として格納され、それぞれがその部分を参照することになるので、参照先が同一でこのような結果になります。
<検証6>
検証5と同じなのですが、Integerの場合は値により、通常の変数となるか、コンスタントプールへの参照となるかが分かれるようです。
127までは、定数扱いでコンスタントプールに格納されますが、128以上だと別インスタンス扱いになります。
以下に、実行結果とTestクラスのコードを添付します。