はじめに
javaのオーバーライドを深堀していたらややこしくなってしまったので、整理しておきます。
コード例
class A{
List<Number> method(Number i){
return new ArrayList<Number>();
}
}
class B extends A{
//例①
List<Number> method(Number i){
return new ArrayList<Number>();
}
//例②
ArrayList<Number> method(Number i){
return new ArrayList<Number>();
}
//例③
List<Integer> method(Number i){
return new ArrayList<Number>();
}
//例④
List<Number> method(Integer i){
return new ArrayList<Number>();
}
}
Bクラス内に、親クラスAのmethodメソッドを実装した4例があります。
例①:戻り値・戻り値のジェネリクス・引数の型すべて一致
例②:戻り値が継承元戻り値のサブクラス
例③:戻り値のジェネリクスが継承元ジェネリクスのサブクラス
例④:引数の型が継承元引数のサブクラス
この中で、コンパイルが通る例はどれでしょう?
(すべて実装するのではなく、一つを選んで実装した場合と解釈してください)
正解は、例①、例②、例④ です。
解説
オーバーライドの条件は以下です。
メソッド名:
スーパークラスのメソッド名と完全に一致
引数:
スーパークラスのメソッドのシグネチャ(引数の型、数、並び)が完全に一致
戻り値の型:
スーパークラスのメソッドの戻り値の型と一致、または、スーパークラスの戻り値の型のサブクラス
これをもとに、例を見ていきましょう。
例①:戻り値・戻り値のジェネリクス・引数の型すべて一致
>コンパイル可能。
メソッド名・シグネチャ・戻り値型が完全に一致しているため、問題なくオーバーライドできます。
例②:戻り値が継承元戻り値のサブクラス
>コンパイル可能。
ArrayListはListのサブクラスのため、こちらも問題なくオーバーライドできます。
例③:戻り値のジェネリクスが継承元ジェネリクスのサブクラス
>コンパイル不可。
ここがややこしいところです...
ジェネリクス型は不変であるため、サブクラスであっても互換性はありません。
そのため、ArrayList<A>とArrayList<B>に関係はなく、コンパイルエラーとなってしまいます。
例④:引数の型が継承元引数のサブクラス
>コンパイル可能。ただし、オーバーライドではない。
さらにややこしいところです.....
先述した通り、オーバーライドはシグネチャの完全一致が条件です。
そのため、型がサブクラスであったとしても関係なくオーバーライドにはなりません。
ただし、今回の場合オーバーロードが成立するため、コンパイルは問題なく通ることになります。
おわりに
学べば学ぶほどややこしくなりますね、、
ここは基礎的な部分なので、しっかりと抑えたいと思います。