はじめに
オブジェクト指向プログラミングにおいて、複数のオブジェクト(インスタンス)を比較する際には注意が必要です。
オブジェクトの比較には、「同一性」と「同値性」二つの観点について比較するパターンがあります。まずは、「同一性」と「同値性」について、簡単にまとめてみましょう。
- 同一性:同一のオブジェクトを、複数の変数が参照しているという性質
- 同値性:異なるオブジェクトであるが、同じ値を持っているという性質
オブジェクトの持つ値を比較するのか、変数が参照しているオブジェクトを比較するのか…。言葉は似ていますが、この二つの観点は比較対象が異なる点に注意してください。
この記事では、Java言語におけるオブジェクトの比較についてお話しします。
Java言語におけるオブジェクトの比較
皆さんはJava言語の中でオブジェクトの比較を行う際、どのような方法を取るでしょうか。「==演算子」を用いた比較と「equals()メソッド」を用いた比較、この二つの内どちらかが思い浮かんだ人もいるのではないでしょうか。
実はこの二つの方法、先ほど紹介した「同一性」と「同値性」の二つの観点から、それぞれ比較対象が異なります。では、二つの比較方法の違いについてみていきましょう。
まずは、「==演算子」を用いた比較です。サンプルコードをご覧ください。
==演算子
1 2 3 4 5 6 7 8 9 10 |
public class Sample { public static void main(String[] args) { String str1 = new String("KENスクール"); String str2 = "KENスクール"; //「==演算子」を用いた比較 System.out.println(str1 == str2); } } |
「==演算子」を用いて、Stringオブジェクトを比較しています。Stringクラスはnew演算子を用いる方法、そしてダブルクォーテーションのみを用いる方法、二つの方法でオブジェクトの生成を行うことができます。
今回は“KENスクール”という同じ文字列を持つStringオブジェクトを二つ生成していますが、こちらのコードの実行結果は、falseとなります。これは「==演算子」が、オブジェクトの「同一性」を比較するからです。ひも解いていきましょう。
サンプルコード9行目で比較している変数の中身に注目してください。
今回比較している変数str1、str2は参照型変数です。そのため、変数の中にはインスタンス化によって生成されたオブジェクトのアドレス番号が代入されます。
「==演算子」を用いた比較は、変数の中身の比較を行います。今回でいうと、アドレス番号の比較を行っています。つまり、比較した変数が同一のオブジェクトを参照しているかどうかの比較をしています。
次の図はメモリ上でのイメージ図になります。
※図内で使用しているメモリのアドレス番号は例であり、実際のものとは異なります。
今回のサンプルコードでは、二つのオブジェクトが生成される場所(アドレス番号)が異なります。そして生成された変数は、代入されたアドレス番号をもとにそれぞれのオブジェクトを参照しているのです。つまり、今回は同一のオブジェクトを参照していないとして、結果がfalseとなりました。
【実行結果】
false
では、trueを得るためにはどうすればいいのか。二つの変数が同一のオブジェクトを参照するように誘導すればいいのです。
1 2 3 4 5 6 7 8 9 |
public class Sample { public static void main(String[] args) { String str1 = "KENスクール"; String str2 = "KENスクール"; //「==演算子」を用いた比較 System.out.println(str1 == str2); } } |
例えば、すべてのStringオブジェクトのインスタンス化を、new演算子を用いずに行わせてみます。こうすることで、二つの変数はコンスタントプールの働きにより、同一のオブジェクトを参照します。よってtrueを得られるようになります。
【実行結果】
true
※コンスタントプールについてはここでは深く触れません。Stringオブジェクトはnew演算子を使わずにインスタンス化を行うと、同じ文字列を持つオブジェクトが既に存在する場合、新たなオブジェクトを生成せずに既存のオブジェクトを参照させる性質を持つ。と押さえておいてください。(new演算子を用いたインスタンス化では別々のオブジェクトを生成します。) |
メモリ上でのイメージはこのような形です。
※図内で使用しているメモリのアドレス番号は例であり、実際のものとは異なります。
以上が「==演算子」を用いた「同一性」の比較です。
equals()メソッド
次は「equals()メソッド」を用いた比較をみていきましょう。
「equals()メソッド」は、同一のオブジェクトを参照しているかどうか。ではなく同じ値、つまりStringオブジェクトにおいては、同じ文字列を持っているかどうかの比較を行います。
サンプルコードをご覧ください。
1 2 3 4 5 6 7 8 9 |
public class Sample { public static void main(String[] args) { String str1 = new String("KENスクール"); String str2 = "KENスクール"; //「equals()メソッド」を用いた比較 System.out.println(str1.equals(str2)); } } |
6行目までは、初めのサンプルコードと同じですが、9行目で「equals()メソッド」を用いた比較を行っています。こちらのコードの実行結果はtrueになります。「equals()メソッド」によって、それぞれのオブジェクトが持つ文字列データの比較、つまり「同値性」の比較が行われたのです。
【実行結果】
true
ということでJava言語では、
オブジェクトの「同一性」を比較したい場合には「==演算子」、「同値性」を比較したい場合には「equals()メソッド」
を適宜使い分けていきましょう。
…と締めたいところですが、この「equals()メソッド」は、使用の際に大きく注意が必要です。
実は、このメソッドはObjectクラスに定義されているメソッドであり、全てのクラスが保有するメソッドなのです。
どうしてそれが注意なのか、まずはObjectクラスに定義されている「equals()メソッド」を確認してみましょう。
equals()メソッドの注意点
Objectクラスに定義されている「equals()メソッド」は、自身のオブジェクト(this)と、引数に渡された参照先のオブジェクトが、同一かどうかの比較が行われています。
1 2 3 |
public boolean equals(Object obj){ return(this == obj); } |
つまりこの時点での「equals()メソッド」は、「同一性」の比較を行っているのです。
では、どうやって「equals()メソッド」を使って「同値性」の比較を行うのかというと、各クラスが保有する「equals()メソッド」を、使用者が比べたい値を比較するようにオーバーライド(再定義)してあげる必要があります。
「同値性」の比較では、オブジェクトの持つ値の比較と言っても、用途に応じて比較する基準が異なってきます。そのため、このような形式をとっているのです。
中でもStringクラスは特別であり、既にクラス内で「equals()メソッド」のオーバーライドが行われています。だから、何事もなく「同値性」の比較が行われるのです。
試しに「equals()メソッド」がオーバーライドされているStringクラスと、オーバーライドされていないStringBuilderクラスを用いて結果を比較してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class Sample { public static void main(String[] args) { String str1 = new String("KENスクール"); String str2 = "KENスクール"; StringBuilder sb1 = new StringBuilder("KENスクール"); StringBuilder sb2 = new StringBuilder("KENスクール"); //①Stringオブジェクトの比較 System.out.println(str1.equals(str2)); //②StringBuilderオブジェクトの比較 System.out.println(sb1.equals(sb2)); } } |
【実行結果】
①:true
②:false
このように、全てのクラスの「equals()メソッド」が手放しに「同値性」の比較を行ってくれるわけではないので注意してください。
ちなみに、Stringクラスのように、デフォルトで「equals()メソッド」がオーバーライドされているクラスはいくつかあります。簡単にまとめると、ラッパークラスとStringクラスではデフォルトでオーバーライドが成されています。
※ラッパークラスに関してはこちらを参照してください。
まとめ
今回は、オブジェクト指向におけるオブジェクトの比較について、「同一性」と「同値性」の観点からお話させていただきました。なかでもJava言語では「同値性」の比較、つまり「equals()メソッド」を使用する際には注意が必要です。この辺りの内容は資格試験でも問われることがあるので、しっかりマスターしてプログラミングスキルの向上に役立ててみてください。