実践 Java「同一性と同値性」

java

この記事の概要

同一性と、同値性について具体的なコードで動作を確認します。
【キーポイント】
・ ==演算子
・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メソッド

検証内容と検証結果

■クラスの概要

クラス名  説明
Itemidと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なしStringString 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
検証6newなしIntegerInteger 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クラスのコードを添付します。

タイトルとURLをコピーしました