はじめに
この実験では、Java で 2 つのオブジェクトが等しいかどうかをチェックする方法を学びます。オブジェクトの比較における == 演算子と equals() メソッドの基本的な違いを探ります。
まず、組み込みの equals() メソッドを使ってオブジェクトを比較し、さまざまなデータ型での動作を理解します。次に、独自のカスタムクラスで equals() メソッドをオーバーライドして、オブジェクトの論理的な等価性を定義する方法を学びます。最後に、等価性チェックを行う際に null オブジェクトを処理する重要な考慮事項に対処し、潜在的な NullPointerException エラーを防ぎます。
等価性のために equals() メソッドを使用する
このステップでは、Java で equals() メソッドを使ってオブジェクトを比較する方法を探ります。== 演算子は 2 つのオブジェクト参照がメモリ内のまったく同じオブジェクトを指しているかどうかをチェックしますが、equals() メソッドは 2 つのオブジェクトが論理的に等しいか、つまり同じ値や状態を表しているかをチェックするように設計されています。
この概念を実証するために、簡単な Java ファイルを作成して始めましょう。
WebIDE を開き、
~/projectディレクトリにいることを確認します。ターミナルのプロンプトを見るか、pwdと入力して Enter キーを押すことで確認できます。~/projectディレクトリにEqualityDemo.javaという名前の新しいファイルを作成します。左側のファイルエクスプローラーで右クリックして「New File」を選択し、EqualityDemo.javaと入力することができます。EqualityDemo.javaファイルをエディタで開き、次のコードを貼り付けます。public class EqualityDemo { public static void main(String[] args) { String str1 = new String("hello"); String str2 = new String("hello"); String str3 = str1; System.out.println("Comparing String objects:"); System.out.println("str1 == str2: " + (str1 == str2)); System.out.println("str1.equals(str2): " + str1.equals(str2)); System.out.println("str1 == str3: " + (str1 == str3)); System.out.println("str1.equals(str3): " + str1.equals(str3)); System.out.println("\nComparing primitive types (int):"); int num1 = 10; int num2 = 10; System.out.println("num1 == num2: " + (num1 == num2)); } }このコードでは:
- 同じ内容("hello")を持つ 2 つの
Stringオブジェクトstr1とstr2をnew String()を使って作成します。これにより、メモリ内に別々のオブジェクトが作成されます。 - 3 つ目の
String参照str3を作成し、str1と同じオブジェクトを指すようにします。 ==とequals()の両方を使ってstr1とstr2、およびstr1とstr3を比較します。==を使ってプリミティブ型のintを比較する例も示しています。equals()はオブジェクトに対して使用され、プリミティブ型には使用されないことを覚えておいてください。
- 同じ内容("hello")を持つ 2 つの
EqualityDemo.javaファイルを保存します(Ctrl+S または Cmd+S)。WebIDE の下部にあるターミナルを開きます。
次のコマンドを入力して Enter キーを押し、Java プログラムをコンパイルします。
javac EqualityDemo.javaエラーがなければ、何も出力されないはずです。
次のコマンドを入力して Enter キーを押し、コンパイルされたプログラムを実行します。
java EqualityDemo次のような出力が表示されるはずです。
Comparing String objects: str1 == str2: false str1.equals(str2): true str1 == str3: true str1.equals(str3): true Comparing primitive types (int): num1 == num2: truestr1 == str2がfalseになるのは、メモリ内で異なるオブジェクトであるためです。ただし、str1.equals(str2)はtrueになります。なぜなら、Stringクラスのequals()メソッドは文字列の実際の内容を比較するようにオーバーライドされているからです。str1 == str3はtrueになります。なぜなら、str3はstr1とまったく同じオブジェクトを指しているからです。
これは、Java でオブジェクトを比較する際の ==(参照の等価性)と equals()(論理的な等価性)の重要な違いを示しています。プリミティブ型には、== が値の比較に使用されます。
カスタムクラスで equals() をオーバーライドする
前のステップでは、String オブジェクトに対する equals() メソッドの動作を見ました。String クラスは、内容に基づく意味のある比較を行うために、デフォルトの equals() メソッド(Object クラスから継承されたもの)をオーバーライドしています。
しかし、独自のカスタムクラスを作成する場合、Object から継承されたデフォルトの equals() メソッドは単に == 演算子を使用します。つまり、参照の等価性のみをチェックします。カスタムクラスのオブジェクトをその属性に基づいて(論理的な等価性で)比較するには、自分で equals() メソッドをオーバーライドする必要があります。
このステップでは、簡単な Person クラスを作成し、その equals() メソッドをオーバーライドします。
WebIDE で
~/projectディレクトリにいることを確認します。~/projectディレクトリにPerson.javaという名前の新しいファイルを作成します。Person.javaを開き、Personクラスの次のコードを貼り付けます。public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } // Default equals() method (inherited from Object) would only check reference equality // We need to override it to check for logical equality based on name and age @Override public boolean equals(Object obj) { // Step 1: Check if the objects are the same instance if (this == obj) { return true; } // Step 2: Check if the object is null or of a different class if (obj == null || getClass() != obj.getClass()) { return false; } // Step 3: Cast the object to the correct type Person person = (Person) obj; // Step 4: Compare the relevant attributes return age == person.age && name.equals(person.name); // Use equals() for String comparison } }この
Personクラスでは:- 2 つのプライベート属性
name(String型)とage(int型)があります。 - これらの属性を初期化するコンストラクタがあります。
- 属性にアクセスするためのゲッターメソッドがあります。
equals()メソッドをオーバーライドしています。オーバーライドされたequals()メソッド内のステップを見てみましょう。if (this == obj):これは最適化です。2 つの参照がまったく同じオブジェクトを指している場合、それらは確かに等しいです。if (obj == null || getClass() != obj.getClass()):これは、比較対象のオブジェクトがnullであるか、Personクラスのインスタンスでないかをチェックします。どちらかが真であれば、それらは等しくないはずです。Person person = (Person) obj;:汎用のObjectをPersonオブジェクトにキャストして、そのnameとage属性にアクセスできるようにします。return age == person.age && name.equals(person.name);:これが論理的な比較の核心です。ageが同じであること(プリミティブ型のintには==を使用)、およびnameが同じであること(Stringオブジェクトにはequals()を使用)をチェックします。
- 2 つのプライベート属性
Person.javaファイルを保存します。次に、オーバーライドした
equals()メソッドをテストするために、PersonEqualityDemo.javaという別のファイルを作成しましょう。このファイルを~/projectディレクトリに作成します。PersonEqualityDemo.javaを開き、次のコードを貼り付けます。public class PersonEqualityDemo { public static void main(String[] args) { Person person1 = new Person("Alice", 30); Person person2 = new Person("Alice", 30); Person person3 = new Person("Bob", 25); Person person4 = person1; System.out.println("Comparing Person objects:"); System.out.println("person1 == person2: " + (person1 == person2)); System.out.println("person1.equals(person2): " + person1.equals(person2)); System.out.println("person1 == person3: " + (person1 == person3)); System.out.println("person1.equals(person3): " + person1.equals(person3)); System.out.println("person1 == person4: " + (person1 == person4)); System.out.println("person1.equals(person4): " + person1.equals(person4)); } }このデモクラスでは、いくつかの
Personオブジェクトを作成し、==とオーバーライドしたequals()メソッドの両方を使用して比較します。PersonEqualityDemo.javaファイルを保存します。ターミナルを開きます。
~/projectディレクトリにいることを確認します。両方の Java ファイルをコンパイルします。一度に複数のファイルをコンパイルすることができます。
javac Person.java PersonEqualityDemo.javaこれにより、
Person.classとPersonEqualityDemo.classファイルが作成されるはずです。デモプログラムを実行します。
java PersonEqualityDemo次のような出力が表示されるはずです。
Comparing Person objects: person1 == person2: false person1.equals(person2): true person1 == person3: false person1.equals(person3): false person1 == person4: true person1.equals(person4): true予想通り、
person1 == person2はfalseです。なぜなら、それらは別々のオブジェクトだからです。しかし、person1.equals(person2)はtrueです。なぜなら、オーバーライドしたメソッドによると、それらのnameとageが同じだからです。person1とperson3はどちらの比較方法でも等しくありません。person1とperson4は、同じオブジェクトを参照しているため、どちらの比較方法でも等しいです。
equals() メソッドをオーバーライドすることで、カスタムクラスのオブジェクトに対して、単なるメモリ位置ではなく、論理的な状態に基づいて「等しい」という意味を定義することができます。
等価性判定における null オブジェクトの扱い
前のステップでは、Person クラスの equals() メソッドをオーバーライドして、オブジェクトを属性に基づいて比較することに成功しました。堅牢な equals() メソッドを記述する上で重要な点の 1 つは、潜在的な null 値を適切に扱うことです。null オブジェクトに対してメソッドを呼び出そうとすると、NullPointerException が発生します。これは Java でよく見られるエラーです。
Person.java でオーバーライドした equals() メソッドには、既に null チェックが含まれています。if (obj == null || getClass() != obj.getClass()) という部分です。これは、比較対象のオブジェクトが null である場合を扱う標準的な方法です。
このステップでは、オブジェクトを null と比較したときに何が起こるかを示し、equals() メソッドが正しく処理することを確認します。
WebIDE で
~/projectディレクトリにいることを確認します。前のステップで作成した
PersonEqualityDemo.javaファイルを開きます。既存の比較文の後に、
mainメソッドに次の行を追加します。System.out.println("\nComparing with null:"); System.out.println("person1.equals(null): " + person1.equals(null));このコードは、単に
person1とnullの比較を追加するだけです。PersonEqualityDemo.javaファイルを保存します。ターミナルを開きます。
~/projectディレクトリにいることを確認します。変更した
PersonEqualityDemo.javaファイルをコンパイルします。javac PersonEqualityDemo.java変更したファイルだけを再コンパイルすればよいことを覚えておいてください。このステップでは
Person.javaは変更されていないので、PersonEqualityDemo.javaだけをコンパイルすればいいです。コンパイルしたプログラムを実行します。
java PersonEqualityDemoこれまでの出力の後に、
nullとの新しい比較結果が表示されるはずです。Comparing Person objects: person1 == person2: false person1.equals(person2): true person1 == person3: false person1.equals(person3): false person1 == person4: true person1.equals(person4): true Comparing with null: person1.equals(null): falseperson1.equals(null): falseという出力は、オーバーライドしたequals()メソッドがnullとの比較を正しく処理し、NullPointerExceptionを投げることなくfalseを返すことを確認しています。これは、Personクラスのequals()メソッドのif (obj == null || getClass() != obj.getClass())という行が、objの属性にアクセスしようとする前にnullをチェックするからです。
null を適切に扱うことは、Java で堅牢なコードを書く上で重要な部分です。特にオブジェクトの比較を行うときには、オーバーライドした equals() メソッドの最初に null チェックを必ず含めてください。
まとめ
この実験では、Java で 2 つのオブジェクトが等しいかどうかをチェックする方法を学びました。まず、参照の等価性をチェックする == 演算子と、論理的な等価性をチェックする equals() メソッドの違いを理解しました。これを String オブジェクトとプリミティブ型を使って実証し、オブジェクトに対して == と equals() が異なる動作をすることを確認しました。
次に、カスタムクラスで equals() メソッドをオーバーライドして、オブジェクトの等価性を判断する独自の基準を定義する方法を探りました。これは、カスタムオブジェクトがメモリ位置ではなく、内容や状態に基づいて比較されるようにするために重要です。最後に、equals() メソッド内で null オブジェクトを適切に扱うことの重要性を学びました。これにより、NullPointerException を防ぎ、堅牢な等価性チェックを行うことができます。



