今回はJavaにおいて「同じ」という概念についての話です。
同じという言葉の2つの異なる意味
- 同じインスタンスを参照していること
- インスタンスは異なるけれど、値は同じであること
前者を「同一」であると呼び、後者を「同値」であると呼びます。
同一について
Object hoge = new Object();
Object huga = hoge; //hogeの参照をコピーしてhugaに代入
このとき、hogeとhugaは同一である、と言います。
同一性の判定は ==演算子で判定します。
例えば、
Object hoge = new Object();
Object huga = new Object();
huga = hoge;
としても、hogeとhugaは同一ではありません(異なるインスタンスだから)。
同値について
public class Cat{
private String name;
public Cat(String name) {
this.name = name;
}
}
というクラスを使って、同じnameの値を持った2つのインスタンスを作成します。
Cat hoge = new Cat("neko");
Cat huga = new Cat("neko");
前項で述べたようにこの2つのインスタンスは参照先が異なるので同一ではありません。しかし、それぞれの参照先は同じ値を持っています。
このような状態をhogeとhugaは同値である、と言います。
同値性の判定は ==ではできません。hoge==hugaとするとfalseが返ってきます。
同値性の判定
同値性の判定にはequalsメソッドを使います。ただし、Objectクラスに定義されているequalsメソッドは同一性を確認する実装になっています。
public boolean equals(Object obj){
return (this == obj);
}
したがって、異なるインスタンスどうしが同じ値を持っているかどうか(同値かどうか)は、equalメソッドをオーバーライドすることで判定します。
public static class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
public boolean equals(Object obj) {
if (obj == null){
return false;
}
if (obj instanceof Cat){
Cat cat = (Cat) obj;
return cat.name == this.name;
}
return false;
}
}
とすれば、hoge.equals(huga)などとすれば同値性を判定できます。
ちなみに、equalメソッドのオーバーライドの書き方によって、複数の値を持つクラスで、どちらか片方の値が一致すれば同値とみなす、という判定もできます。
文字列リテラルの同一、同値について
String型は参照型のデータ型ですが、インスタンスは文字列リテラル(""で囲む)だけで作ることができます。
この文字列リテラルで作られたインスタンスは例外で==演算子で同値性の判定をすることができます。
public static void main(String[] args) {
String a= "neko";
String b = "neko";
System.out.println(a == b); //trueで返ってくる
}
これは文字列リテラルが定数値としてインスタンスとは異なる定数用のメモリ空間に作られ、参照を使い回しするための「コンスタントプール」という仕組みがあるためです。
ただし、これは文字列リテラルを使用した時のみなので、Sring型の変数をnew演算子で作った場合は==演算子で同値性の判定はできません。
参考文献
徹底攻略Java SE11 Silver問題集