• ベストアンサー

java ジェネリックスに関して

以前にも似たような質問をしたことがありますが、それに関しての質問です。 次のようなプログラムを書きました。 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が同じではないので、オーバーライド等価ではない、従ってオーバーライドできないと思ったのですが、どうしてオーバーライドできるのでしょうか。

  • Java
  • 回答数2
  • ありがとう数4

質問者が選んだベストアンサー

  • ベストアンサー
  • kana_m
  • ベストアンサー率40% (26/65)
回答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 を持っているのでオーバーライドになるけど、コーディング上はオーバーロードなので矛盾が生じます みたいな感じでしょうか。 質問の意図を汲み違えてたらごめんなさい。

pikacha
質問者

お礼

なるほど! ご回答ありがとうございます。 まさに質問の意図の通りです。 つまり、Aクラスのdisplay(T)は、コンパイルの時にはTはStringクラスとして扱われるため,Testクラスのdisplay(Object)はAクラスのdisplay(T)をオーバーロードしている。しかし、実行時にはそれぞれのdisplayメソッドのシグネチャが一致してしまうためオーバーライド扱いになってしまう。 という矛盾が生じているのですね。 ここの部分がまさに分からなかったので、疑問がはれました! 前回に続き、ご回答ありがとうございました。

その他の回答 (1)

noname#94983
noname#94983
回答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>はジェネリクスとして無意味だ。 「なぜ、フィールドやメソッドだけでなく、クラスにもジェネリクスを指定するのか」を考えれば、自然と理解できるんじゃないかと思う。

pikacha
質問者

お礼

ご回答ありがとうございます。 Testクラスのdisplay(Object)がなぜ、Aクラスのdisplay(T)をオーバーライドできないのかという疑問がはれました。 コンパイル時にはTはString型として認識されていますが、実行時にはerasureに基づいてObject型に置き換えられるためオーバーライドとオーバーロードの矛盾が生じるということだったのですね。 今後も「クラスにジェネリックスを指定する」意味を考えながらジェネリックスを勉強していきます!

関連するQ&A

  • ジェネリックスに関して

    次のようなプログラムを書きました。 class A<T> {  public String display(T t) {   return t.toString();  } } public class Test extends A<String> {  public String display(Object o) { //ここでコンパイルエラー   return o.toString();  }  public static void main(String[] args) {   Test test = new Test();    test.display("hello, world");  } } 上で示した部分でコンパイルエラーが出ました。エラー内容は 名前の競合:型Testのメソッドdisplay(Object)は型A<T>のメソッドdisplay(T)と同じerasureを持っていますが、オーバーライドしません。 というものでした。この場合、 public class Test extends A<String> と、TがStringであることを明示しているので、このプログラムではdisplay(Object)はdisplay(T) をオーバーロードしているのではないのでしょうか。 もしくは、もしdisplay(Object)とdisplay(T)が同じerasureを持っているなら、それでオーバーライドしていることにはならないのでしょうか?

    • ベストアンサー
    • Java
  • オーバーライドの必要性

    まだまだJavaを勉強している途中なのですが、下記のサンプルコードでオーバーライド(メソッドをオーバーロードすることをオーバーライドというのでしょうか…!?)のの必要性がよく分からなくなってしまったので質問させて頂きました。 /* sample.java */ abstract class super_class{  void write(){} } class CLASS1 extends super_class{  void write(){ System.out.println("CLASS1"); } } class CLASS2 extends super_class{  void write(){ System.out.println("CLASS2"); } } class sample{  public static void main(String args[]){   super_class obj = new CLASS2();   obj.write();   obj = new CLASS1();   obj.write();  } } 以上のコードを実行した場合 CLASS2 CLASS1 と表示されるのは多少なりとも勉強して分かったつもりでした…。 しかし、あくまでサンプルであるために特に意味がないことでもオーバーライドの説明をしているのだろうという風に考えてしまったのですが、 結局は、 スーパークラスとサブクラスに同じシグネチャのメソッドがあった場合、各サブクラスのメソッドが実行される と言うことは、スーパークラスのwrite()メソッドは何をしているのでしょうか? 多様性はJavaでも大切なものであるとのことなので質問させて頂きました。 なるべくわかりやすく説明して頂きたいと思います。 どうかお答えをお願いします。

    • ベストアンサー
    • Java
  • メソッドのオーバーライド(java)

    class A3{ void hello(){ System.out.println("A3"); } void hello(int i){ System.out.println("A3"+i); } } class B3 extends A3{ void hello(){ System.out.println("B3"); } } class C3 extends B3{ void hello(String s){ System.out.println("C3"+s); } } class MethodOverriding3{ public static void main(String args[]){ A3 obj = new C3(); obj.hello(); } } 上のプログラムを実行すると"B3"と表示されまが、どうしてクラスBのメソッドが実行されるのでしょうか? クラスAのメソッドが無視される仕組みがわかりません。 また、クラスMethodOverriding3でobj.hello("abc")としてコンパイルすると mo.java:25: シンボルを見つけられません。 シンボル: メソッド hello(java.lang.String) 場所 : A3 の クラス obj.hello("abc");   ^ エラー 1 個 とエラーが出ます。 どうしてでしょうか? 誰か教えてください、お願いします。

    • ベストアンサー
    • Java
  • Javaについて教えてください!

    OyaクラスのメインメソッドからKoクラス内のメソッドを呼び出して文字列の出力を行いたいのですが、今のままではエラーが出てコンパイル出来ません。どこが間違っているか教えてください!ちなみにエクリプスでの実行には問題はありません。 public class Ko{ public static void ko(){ String str = "KO"; System.out.println(str); }} public class Oya{ public static void main(String[] args){ Ko.ko(); }}

    • ベストアンサー
    • Java
  • java 実行できない

    javaで public class HelloWorld{ public static void main(String[] args){ System.out.println("Hello World!"); } } のようなプログラムをかいて、コンパイルして、実行しようとすると エラー: メイン・メソッドがクラスStringで見つかりません。次のようにメイン・メソッドを定義してください。 public static void main(String[] args) またはJavaFXアプリケーション・クラスはjavafx.application.Applicationを拡張する必要がありま というエラーがでます。コンパイルはできます。jdkは1.8.0_25を使っています。

  • Javaのプログラムについて至急教えてください!

    //Sub.java class Super{ public void A(){} } class Sub extends Super{ /* (1) */ } このプログラムの(1)に入るものを下から2つ選んで他のクラスのメインメソッドから呼び出すのですがどうやってもわかりません。わかる方教えてください! public void A(){} public String A(){ return "pool";} public int A(String str){return Integer.parseInt(st);} public int A(){return 3;} public Object A(){return new Object();}

    • ベストアンサー
    • Java
  • javaのプログラムについての質問です。

    /* * 引数、パラメータ、戻り値を使用して * SubTest19クラスでフィールド変数を使用しないよう、 * 修正して下さい。 * * * また、SubTest19クラスのput()で行っている出力はfor文を使用する事。 * */ class Test19 {   public static void main(String[] args) {   // SubTest19クラスのインスタンス化   SubTest19 st19 = new SubTest19();   // setメソッド呼び出し:戻り値&引数無し   st19.set();   // putメソッド呼び出し:戻り値&引数無し   st19.put();   } } // SubTest19クラス class SubTest19 {   public Test19data t19d[] = null;       // setメソッド    public void set(){     t19d = new Test19data[3];     t19d[0] = new Test19data();     t19d[1] = new Test19data();     t19d[2] = new Test19data();     t19d[0].px = "K-0963";     t19d[0].ct = 23;     t19d[1].px = "T-523363";     t19d[1].ct = 517;     t19d[2].px = "J-97553";     t19d[2].ct = 5;   }     // putメソッド   public void put(){      System.out.println(t19d[0].px);     System.out.println(t19d[0].ct);     System.out.println(t19d[1].px);     System.out.println(t19d[1].ct);     System.out.println(t19d[2].px);     System.out.println(t19d[2].ct);   } } class Test19data{   // フィールド変数の宣言:String,int    public String px;    public int ct;    public Test19data(){       px = "";      ct = 0;    } } このような問題なのですが、どうしても出来ずに手詰まり状態です。丸投げで非常に申し訳ないのですが、どなたか途中までコードを書いて頂けないでしょうか?よろしくお願い致します。

  • javaの質問です。

    明日JavaのBronze試験を受けに行くのですが、全然わからずに困っています。 紫色のJavaプログラマBronzr[SE7]という本を使用しています。 本の模試の問題です。 37 Class Super{ static void method(){ System.out.println("Super"); } } Class Sub extends Super{ static void method(){ System.out.println("Sub"); } } Class Test { public static void main(String[] args){ Super obj = new Sub(); obj.method(); } } この問題はSuperが表示されるらしいのですが、理由がわかりません。 サブクラスをインスタンス化しているので、Subが表示されるというのなら理解できます。staticが関係あるのはなんとなくわかるのですが、、、、、、なぜSuperなのかがわかりません。 解答ではスーパークラスの型にサブクラスを入れているからと簡単に書いてあります。 詳しく説明していただけると助かります。

    • ベストアンサー
    • Java
  • 親クラスから子クラスへアクセス。

    親クラスから子クラスへアクセス。 お世話になります。 以下のプログラムについて考えています。 public class Main{  public static void main(String[] args) {   SubA subA = new SubA();   SubB subB = new SubB();   Super[] subs = {subA,subB};   subs[1].method();   subs[2].method();  } } class Super{  String str = "親";  public void method(){   System.out.println(str);  } } class SubA extends Super{  String str = "子A"; } class SubB extends Super{  String str = "子B"; } この場合、出力結果は当然 親 親 になります。 これを子クラスの str を表示するようにしたいのです。つまり 子A 子B です。 ただし、子クラスで method() をオーバーライドしたり、super.str = "子A" のように親クラスを書きかえずにです。 子クラスが何百種類もある場合、全く同じmethod()を書く無駄や、変更があったときにそれを全てを修正しなければならないことを避けたいのです。 何か良案がありましたら、ぜひご教授ください。よろしくお願いします。

  • Javaのプログラムについて教えてください!

    //Sub.java package prac; class Super{ public void A(){ String address = new String("Osaka"); System.out.println(address); } } public class Sub extends Super{ public void A(){ String address = new String("Kyoto"); System.out.println(address); } public int A(String tmp){ return Integer.parseInt(tmp); } } このプログラムを他のクラスのmain関数から呼び出す場合どのようなコードになりますか?また、間違えているところがあれば合わせて教えてください。

    • ベストアンサー
    • Java

専門家に質問してみよう