Equals Method Overloading - Java Puzzlers Advent Calendar1日目
Equals Method Overloading
import java.util.*; class Student { private int id; Student(int id) { this.id = id; } public boolean equals(Student student) { return student != null && student.id == id; } @Override public int hashCode() { return Objects.hash(id); } } public class Main { public static void main(String[] args) { List<Student> students = new ArrayList<>(); students.add(new Student(1)); students.add(new Student(2)); students.remove(new Student(1)); System.out.println(students.size()); } }
上のコードを実行した時の結果はどれになるでしょうか?
- 1
- 2
- コンパイルエラー
- 実行時エラー
答え
選択肢2の2が出力されます。
解説
Studentを受け取るequalsを作れば比較する時クラスの比較すれば良くない?みたいな話を聞いたのでそれを問題にしてみました。
なぜ2が呼ばれているかというとremove内で比較するときにequals(Object)のほうが呼ばれているからです。 以下のコードを考えてみます。
Student student1 = new Student(1); Object student2 = new Student(1); student1.equals(student2); //false
上記のコードを実行したとき、変数の型でどのメソッドを呼び出すか決めているのでObject型のequalsが呼び出されます。
では、次に問題のコードをデコンパイルしてみます。
public static void main(String[] args) { ArrayList students = new ArrayList(); students.add(new Student(1)); students.add(new Student(2)); students.remove(new Student(1)); System.out.println(students.size()); }
コンパイル後だと型の情報が消えてraw型になっています。 あとは内部でequalsが使用されたときObject#equals(Object)されていることがわかると思います。
解決策
今にオーバーロードしてもObject#equals(Object)が使用されるケースがあるので、必ずObject#equals(Object)をオーバーライドするようにしてください。
class Student { private int id; Student(int id) { this.id = id; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return id == student.id; } @Override public int hashCode() { return Objects.hash(id); } }