以前にも似たような質問をしたことがありますが、それに関しての質問です。
次のようなプログラムを書きました。
class A<T> {
public void display(T t) {
System.out.println("A class");
}
}
public class Test extends A<String> {
public void display(Object t) { //問題の行
System.out.println("Test class");
}
public static void main(String[] args) {
}
}
上記の問題の行のところでエラーが出ました。
名前の競合: 型 Test のメソッド display(Object) は型 A<T> の display(T) と同じ erasure を持っていますが、オーバーライドしません
A<T>のメソッドdisplay(T)のerasureはdisplay(Object)になるので、display(T)は確かにTestのメソッドdisplay(Object)と同じerasureを持っています。しかしそうなると、Testのdisplay(Object)のシグネチャがA<T>のメソッドdisplay(T)のシグネチャのerasureと同じになるため、オーバーライドできることになると考えたのですが、コンパイル結果はエラーとなってしまいました。
どうしてオーバーライドできないのでしょうか。
例えば
public class Test extends A<String>
を
public class Test<T> extends A<T>
にかえた場合はうまく行きました。従ってTをStringと指定しているところに問題があると思うのですが、どうしてコンパイルできないのでしょうか。
また、Testのdisplay(Object)をdisplay(String)にかえた場合(このとき、他の部分ははじめのプログラムと同じ)、A<T>クラスのdisplay(T)をオーバーライドできました。今度はTestクラスのdisplay(String)とA<T>クラスのdisplay(T)はerasureが同じではないので、オーバーライド等価ではない、従ってオーバーライドできないと思ったのですが、どうしてオーバーライドできるのでしょうか。
No.2ベストアンサー
- 回答日時:
継承時にパラメータ型を指定しているからです。
Testクラスで継承するAクラスのパラメータ型にStringを指定したので、
Testクラス内ではAクラスのT型情報は全てString型として扱われます。
コンパイル後は確かに型情報は失われるためObject型ではありますが、
コンパイル前にStringの型チェックが行われるため実質String型です。
つまり、Testクラスの親のAクラスのdisplayメソッドの引数は
String型。
そのためTestクラスのdisplayメソッドはオーバーロード扱い。
でも実際にコンパイルされるとAクラスのdisplayメソッドの引数はObject型になるので
Testクラスのdisplayメソッドはオーバーライド扱いになる。
という矛盾が生じます。
名前の競合: 型 Test のメソッド display(Object) は型 A<T> の display(T) と同じ erasure を持っていますが、オーバーライドしません
のエラーを適当な感じに要約すると
名前の競合: 型 Test のメソッド display(Object) は型 A<T> の display(T) と同じ erasure を持っているのでオーバーライドになるけど、コーディング上はオーバーロードなので矛盾が生じます
みたいな感じでしょうか。
質問の意図を汲み違えてたらごめんなさい。
なるほど!
ご回答ありがとうございます。
まさに質問の意図の通りです。
つまり、Aクラスのdisplay(T)は、コンパイルの時にはTはStringクラスとして扱われるため,Testクラスのdisplay(Object)はAクラスのdisplay(T)をオーバーロードしている。しかし、実行時にはそれぞれのdisplayメソッドのシグネチャが一致してしまうためオーバーライド扱いになってしまう。
という矛盾が生じているのですね。
ここの部分がまさに分からなかったので、疑問がはれました!
前回に続き、ご回答ありがとうございました。
No.1
- 回答日時:
public class Test extends A<String> {
public void display(String t) {
これなら、問題はない。あるいは、
public class Sample extends A<Object> {
public void display(String t) {
とかでもOKだ。A<T>を、Test extends A<String>として継承しているわけだから、<T>は、<String>でなければならない。<String>をスーパークラスの<Object>に置き換えることはできない。だからdisplay(T t)はdisplay(Object t)にはできない。
逆に、「extends A<Object>」として、display(String t)はできる。「TはObject」だが、StringはObjectとして扱うことができるから問題はない。
おそらく、A<T>のTと、display(T t)のTは、どちらも同じクラスを示すものである、ということを忘れているんじゃないだろうか。何のために、(実際にそれが利用されているメソッドやフィールドなどではなく)クラスに<T>が指定されるかといえば、それは「このクラスで、<T>という(現時点では不特定だが、実行時には特定のクラスに決定される)クラスが使われる」ということを示すため。その「不特定なクラス」を実際に使用しているのがdisplayのTになる。
つまり、このdisplayのTは、class A<T>で指定したTが実際に使われているところ、というわけになる。だから、両者は同じクラス指定となるのが当然だし、クラスで指定された<T>とあえて異なるクラスを指定するのであれば、そもそも「クラスの<T>の指定が正しくされていない」ということになる。そもそもジェネリクスは「特定のクラスのみを受け付ける」ようにするためのものなわけだから、<Object>はジェネリクスとして無意味だ。
「なぜ、フィールドやメソッドだけでなく、クラスにもジェネリクスを指定するのか」を考えれば、自然と理解できるんじゃないかと思う。
ご回答ありがとうございます。
Testクラスのdisplay(Object)がなぜ、Aクラスのdisplay(T)をオーバーライドできないのかという疑問がはれました。
コンパイル時にはTはString型として認識されていますが、実行時にはerasureに基づいてObject型に置き換えられるためオーバーライドとオーバーロードの矛盾が生じるということだったのですね。
今後も「クラスにジェネリックスを指定する」意味を考えながらジェネリックスを勉強していきます!
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Java Java プログラム public class Main { public static void 3 2023/08/10 23:46
- Java java final 1 2022/06/10 22:49
- C言語・C++・C# Windows Formアプリからコンソールを呼び出して文字を出力させたい 8 2023/05/09 10:53
- Ruby 【JAVA】数字をひし形に出力するプログラムについて 2 2022/07/11 23:32
- C言語・C++・C# C# DatagridviewにExcelシートを反映するとエラーが出る 2 2023/05/06 17:12
- Java java 入力 3 4 3 出力 ABC DEFG HIJ このようなプログラムの書き方を教えてくだ 2 2022/07/15 14:18
- PHP style.cssのjQuery条件付きcssが機能しない 4 2022/07/17 18:27
- HTML・CSS img と p を縦中央に配置したいのですがうまくいきません。 2 2023/01/12 14:38
- JavaScript vertical sliderをautoplayしたい 2 2022/08/25 14:47
- HTML・CSS display: flex; と flex の違い 1 2022/04/25 20:52
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
Java初級 引数に適用できません
-
C# 点の描き方をおしえてくだ...
-
contextってなんですか?
-
c++のキュー
-
C# 他のnamespaceにあるメソッ...
-
オーバーロード、オーバーライ...
-
Strutsでチェックボックスの値...
-
YYYYMMDD書式の日付に対する適...
-
String.containsの反対機能はあ...
-
レコード件数の表示
-
エクセルVBAで、条件に一致する...
-
3年間同じクラスになる確率
-
インスタンス参照でアクセスで...
-
c++,ある関数のクラスから別の...
-
ワイルドカード<?>と型パラメー...
-
「天声人語」をインターネット...
-
DataGridViewでセルクリックイ...
-
string formatについて
-
クラス間でのデータ参照
-
変数の参照でエラーが出てしま...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
Java初級 引数に適用できません
-
public static void main (Stri...
-
レコード件数の表示
-
abstract と static を一緒に付...
-
なぜprotected overrideなのか
-
C# 点の描き方をおしえてくだ...
-
コマンドライン引数のチェック
-
String.containsの反対機能はあ...
-
Google Apps Script で getRang...
-
StringBufferからStringへキャ...
-
Java初心者です、エラーの意味...
-
javaに"search"という関数 or ...
-
C# でメソッドに送られてきたOb...
-
YYYYMMDD書式の日付に対する適...
-
メソッド宣言の戻り値の型にク...
-
return new使用時
-
readLine()ではじめから読み直...
-
シェルスクリプトからのJavaメ...
-
Fileの読み取り専用の解除
-
親の親のメソッドを呼ぶには?
おすすめ情報