【Java】Object.equalsメソッドをオーバーライドしたらArrayList.containsメソッドの中でも使えるようになるのか?【@Override】
こんにちは~!
3回目の投稿です!!
今回は、お仕事の中でいただいたJavaについての質問を題材にお話ししたいと思います。
ちなみに私にとって一番歴が長い言語がJavaです!
Oracle社の認定資格(Silver)も持ってます!
質問の内容は、Javaの基本である「オーバーライド」の応用です。
Object型のequalsメソッドをオーバーライドすることで、
ArrayList型のcontainsメソッド内で利用できるようにならないか、という質問でした。
それでは、順を追って解説していこうと思います!
目次
オーバーライドとは?
オーバーライドについて一言で説明すると、
「スーパークラス(親クラス)のメソッドを継承したサブクラス(子クラス)でアレンジして利用する」
ということになります。
こんな感じです↓
public class Main {
public static void main(String[] args) {
new ClassSub().foo();
}
}
// スーパークラス
class ClassSuper {
public void foo(){
// 本来の処理
System.out.println("ClassSuper");
}
}
// サブクラス
class ClassSub extends ClassSuper{
@Override
public void foo(){
// アレンジしたい処理
super.foo();
System.out.println("ClassSub");
}
}
16行目の「extends ClassSuper」でスーパークラスを継承していますね。
メソッドの定義の前に「@Override」と書いておくことで、
オーバーライドされていない場合(入力ミス等)にエラーで教えてくれます。
「super」を使用することでスーパークラスの本来の処理を呼び出すことが可能なので、
処理を少しだけ変えることもできるし、丸ごと書き換えることもできます。
Object型を継承する場合
本来は「extends」で継承することが必須なのですが、
実はObject型を継承する場合は例外で、「extends Object」と書かなくても継承ができてしまいます!
(これ忘れがちですよね…)
Javaでは「全てのクラスはObject型を暗黙で継承している」ということになっているのです。
言わば全てのクラスのスーパークラスですね。
「extends」を書かなくても、「@Override」を書いてもいいということなのです。
本題:Object.equalsをオーバーライドしてArrayList.containsで使いたい
ここで本題ですが、Object.equalsメソッドをオーバーライドすることで、
それを内部的に利用しているArrayList.containsメソッドで利用できるようになるのでしょうか?
例えば、下記のようなコードを書いてみました↓
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Test> list = new ArrayList<>();
list.add(new Test());
System.out.println(list.contains(new Test()));
}
}
class Test {
@Override
public boolean equals(Object o) {
System.out.println("OK");
return true;
}
}
OK
true
おおっ、ちゃんとサブクラスの「System.out.println(“OK”);」が呼び出され、
「OK」が出力されていますね!
では、こちらはどうでしょうか↓
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Test> list = new ArrayList<>();
list.add(new Test());
System.out.println(list.contains(new Object()));
}
}
class Test {
@Override
public boolean equals(Object o) {
System.out.println("OK");
return true;
}
}
false
おや、今度はサブクラスのequalsが呼び出されておらず、
本来のequalsが呼び出されていますね。
先程のコードとの違いは、7行目の「list.contains(new Test())」が「list.contains(new Object())」となっていること。
ではでは、今度はこちらはどうでしょうか↓
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Test> list = new ArrayList<>();
list.add(new Test());
System.out.println(list.get(0).equals(new Object()));
}
}
class Test {
@Override
public boolean equals(Object o) {
System.out.println("OK");
return true;
}
}
OK
true
今度は7行目でcontainsを使わず、「list.get(0).equals(new Object())」としてみましたが、上手くいきましたね!
比較対象がObject型という点では「list.contains(new Object())」と同じはずなのに…
一体どういうことなのでしょうか?
※補足:今回はequalsの戻り値を無条件でtrueとしているので省略していますが、
equalsをオーバーライドした場合はhashCodeもオーバーライドする必要があります。
参考…Object (Java Platform SE 8 ) (oracle.com)
ArrayList.containsのリファレンスを見てみよう
困ったときは、リファレンスを探して読んでみましょう!
今回読んだArrayList.containsのリファレンスはこちら
つまり、このリストに、(o==null ? e==null : o.equals(e))となる要素eが1つ以上含まれている場合にのみtrueを返します。
ArrayList (Java Platform SE 8) – Oracle
ここでの「o」がcontainsの引数です。
「e」はリストに格納されている方ですね。
つまり、containsで使われるequalsメソッドはcontainsに指定された引数の型に依存するということが分かりました!
List<Test>と定義していようが、listの中身がTest型だろうが、関係なかったんですね;;
まとめ
以上から、ArrayList.containsの引数に指定されたクラスがObject.equalsをオーバーライドしていた場合は、
ArrayList.containsにて当該サブクラスのequalsが呼び出されることが分かりました!
containsで使われるequalsメソッドはcontainsに指定された引数の型に依存するというところがポイントですね。
皆様の参考になれば幸いです!
それでは、今日はこの辺で。