• ベストアンサー

ジェネリクスとワイルドカード型

class TestTest<T extends Number> { ㅤList<? extends Number> list; } これは問題ないのに class TestTest<? extends Number> { ㅤList<T extends Number> list; } これはダメなのはなぜですか。 猿にも分かる解説がほしいです。

  • Java
  • 回答数6
  • ありがとう数1

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

  • ベストアンサー
回答No.1

>class TestTest<T extends Number> { >ㅤList<? extends Number> list; >} まず、「class TestTest<T extends Number>」と「List<? extends Number> list;」の意味が違います。 「class TestTest<T extends Number>」はクラスの定義で、「<T extends Number>」は「型変数の宣言」です。 「List<? extends Number> list;」はオブジェクト変数の生成で、「<? extends Number>」は「型変数へのバインド」です。 なので下記のようにオブジェクト変数「list」にインスタンスを代入する事が可能です。 import java.util.List; import java.util.ArrayList; ~ class TestTest<T extends Number> { List<? extends Number> list = new ArrayList<Number>(); } >class TestTest<? extends Number> { >ㅤList<T extends Number> list; >} ここからは推測です。 >class TestTest<? extends Number> { ↑クラスの定義では「<? extends ~>」、「<? super ~>」、「<?>」は使えないと思われる。 >ㅤList<T extends Number> list; ↑オブジェクト変数の生成では「<T extends Number>」、「<T super Number>」は使えないと思われる。 クラスで仮型パラメータ「T」が設定されてないなら「<T>」はエラーになる。 クラスで仮型パラメータ「T」が設定されているなら、コンパイル時に「T」は実型パラメータとなるので「<T>」はエラーにならない。

oilon11
質問者

お礼

参考書の説明が超絶雑すぎて、総称型はextendsが使えて、ワイルドカード型はsuperも使えるくらいしか書いてなかったので参考になります。「型変数の宣言」と「型変数のバインド」って初めて聞きました。

その他の回答 (5)

回答No.6

これは「オブジェクト変数の生成」に関する説明で、インスタンスの生成とは違います。 >クラスで仮型パラメータ「T」が設定されているなら、コンパイル時に「T」は実型パラメータとなるので「<T>」はエラーにならない。 クラスで仮型パラメータ「T」が設定されているなら、Javaコンパイラーは(「オブジェクト変数の生成」)「List<T> list;」の(「型変数へのバインド」)「<T>」の「T」はコンパイル時に実型パラメータに変換されると"予測"され正常にコンパイルできます。

回答No.5

これは「オブジェクト変数の生成」に関する説明で、インスタンスの生成とは違います。 Javaコンパイラーは(「オブジェクト変数の生成」)「List<T extends Number> list;」の(「型変数へのバインド」)「<T extends Number>」の「T」は抽象度が高いので解決不能としてエラーになります(この場合「T」は不定と言う扱い)。 Javaコンパイラーは(「オブジェクト変数の生成」)「List<? extends Number> list;」の(「型変数へのバインド」)「<? extends Number>」の「?」は(Tより)抽象度が低く具体的と解釈し正常にコンパイルできます。 「?」が(Tより)抽象度が低く具体的とは、「?」(ワイルドカード型)は実際の「Integer」のような型に近いモノで、そのような(この場合「Number」の下位クラスの「Integer」とか、「Long」とか、「Float」とか、「Double」とか)具体的な何か(ワイルドカード型)が定義されていると(Javaコンパイラーは)解釈します(人間から見ると充分 抽象的ですが、あくまでもJavaの仕様です)。 ざっくりと言うと「?」(ワイルドカード型)はベーシックの変数に どんな型でも代入可能なようなイメージに近いです。 もちろん(この場合)「List」の下位クラスでなければならないとか、その要素は「Number」の下位クラスでなければならないとか、制限はありますが。

  • HNEX
  • ベストアンサー率62% (43/69)
回答No.4

すんごいザックリと言うとこんな感じです 上の例 「TestTestというクラスではNumberを継承したクラスを仮にTとして扱うで~」という宣言が class TestTest<T extends Number> です 「Numberを継承した何かよく分からないクラスを要素として扱うListやで~」という宣言が List<? extends Number> list; 下の例 「TestTestというクラスではNumberを継承した何か…をどうするんや…”?”で書いても名前が無いから扱う事ができん!」というのが class TestTest<? extends Number> です。 「Numberを継承したTという要素を持つ、って宣言されてるけど、Tって何や?Tとはどいつの事や!そういう宣言はクラスでしてくれやで~」ってなるのが List<T extends Number> list; です。

回答No.3

>class TestTest<T super Number>{ (classなどでの)「型変数の宣言」では「<T super Number>」などのように「super」は使えないようです。 ちなみに「class」と同様に「interface」も「型変数の宣言」が可能なようです。 「interface」のジェネリクスの制限も「class」と同様の制限のようです。

回答No.2

>回答No.1 amanojaku1 要約すると (classなどでの)「型変数の宣言」では「<? extends ~>」、「<? super ~>」、「<?>」は使えないと思われる。 、(オブジェクト変数の生成、メソッドの帰り値の型定義、メソッドのパラメータなどでの)「型変数へのバインド」では「<T extends Number>」、「<T super Number>」は使えないと思われる。 クラスで仮型パラメータ「T」が設定されてないなら「<T>」はエラーになる。 クラスで仮型パラメータ「T」が設定されているなら、コンパイル時に「T」は実型パラメータとなるので「<T>」はエラーにならない。 つまり、「型変数の宣言」か「型変数へのバインド」かで何が設定できるのかが変わってくるようです。 例えば、下記のようにメソッド「hoge、huga、piyo」と作った場合 >class TestTest2<T extends Number> { >List<? extends Number> hoge(List<? extends Number> list1){ } >List<? extends Number> huga(List<T extends Number> list2){ } >List<T extends Number> piyo( ){ } >} 「メソッドの帰り値の型定義、メソッドのパラメータ」などは「型変数へのバインド」になるので 「List<? extends Number> hoge(List<? extends Number> list1)」はエラーにならない。 「List<? extends Number> huga(List<T extends Number> list2)」のパラメータ「List<T extends Number> list2」はエラーになる。 「List<T extends Number> piyo( ){ }」の帰り値の型定義「List<T extends Number>」はエラーになる。

関連するQ&A

  • 総称型のキャストでエラー

    下の書き方で、※1はエラーにならないのですが ※2ではエラーになります。 ※1がキャスト可能であれば、 ※2も可能だとおもうのですが。 Java7を使っています。 public class Soushou { void test() { List<Kodomo> kodomoList = new ArrayList<Kodomo>(); List<? extends Oya> oyaList = kodomoList; Object o1 = (List<Oya>) oyaList; // ※1 エラーにならない。 Object o2 = (List<Oya>) kodomoList; // ※2 「キャストすることができません」エラーになってしまう。 } } class Oya { // 親クラス } class Kodomo extends Oya { // 親クラスを継承した子クラス }

    • ベストアンサー
    • Java
  • 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
  • Accessで差分取得する方法は? LEFT JOIN エラー

    Accessで差分抽出するSQLを教えてください。 【旧テーブル】t1 id, class, number, value 1, "a", 1, "aka" 2, "a", 2, "aki" 3, "a", 3, "aku" 【新テーブル】t2 id, class, number, value 1, "a", 1, "aka" 2, "a", 2, "更新" 3, "a", 3, "aku" 4, "a", 4, "新規" 5, "b", 1, "新規" 【ダメだったクエリ】 SELECT t2.* FROM t2 INNER JOIN t1 WHERE t1.class IS NULL OR t1.number IS NULL 【望む結果】 4, "a", 4, "新規" 5, "b", 1, "新規" *列 id は各テーブルの主キーですが、新旧テーブル間の関連はありません(リレーションではありません)。

  • 「オブジェクト志向」の考えかたで質問します。

    「オブジェクト志向」の考えかたで質問します。 いろいろと調べると、 ・繼承 ・カプセル化 ・ポリモーフィズム を総称したのが、「オブジェクト」志向と理解しています。 このとき、 たとえば、 指定するクラスの生徒の情報をとりだすようなソースをつくりたい。 仮に、以下をかんがえてみました。 DB処理は、省いてます。 //実行DAOクラス public class StudentDAO extends StudentDBAccessor{ //指定するクラスに属する生徒をとりだす public List getStudentList(int classNumber){ return super.getStudentList(); } //sql文生成 protected String createSqlSelectStudentList(){ StringBuffer sb = new StringBuffer(); return sb.toString(); } //キーワードを設定 public void setDataSqlStudentList(){ } } public abstract StudentDBAccessor extends DBConnector{ protected List getStudentList(){   //DBそうさ } protected abstract String createSqlStudentList(); protected void setDataSqlStudentList(int classNumber); } //DB接続クラス public class DBConnector{   //省略 } //Beanクラス public class StudentFormBean{ private int studentNumber; private String studentName; public void setStudentNumber(int number){ this.studentNumber = number; } public int getStudentNumber(){ return studentNumber; } } よろしくおねがいします。

    • ベストアンサー
    • Java
  • テンプレートクラスとSTLを利用したMyListクラス

    こんにちは。STLのリストを使い自分だけのMyListクラスを作ろうとしたのですが、コンパイルできません。 エラーメッセージは警告 std::list<T>::iterator' : 依存名は型ではありません。 とでます。 ご教授お願いします。 #include<list> #include<iterator> template <class T> class CMyList { public: CMyList(); //virtual ~CMyList(); //bool HasNext(); //T Next(); //void Pushback( T t ); //void EraseCheck(); //T GetFirst(); private: std::list< T > m_List; std::list< T >::iterator m_It;//コンパイルエラー }; template <class T> CMyList<T>::CMyList() { m_List.clear(); std::list< T > ::iterator it = m_List.begin();//こう宣言する分にはOK m_It = m_List.begin(); } int main() { CMyList<int> m_List; return 0; }

  • enum型

    お世話になります。 Javaのenum型に関してお聞きしたいです。 JavaのAPIドキュメントを見ると、enumのvalueOfメソッドに関して、 以下のように記述がありますが、具体的にどのように使用するので しょうか? public static <T extends Enum<T>> T      valueOf(Class<T> enumType,String name) どうかよろしくお願いします。

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

    次のようなプログラムを書きました。 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
  • 継承について

    以下のような問題(SJC-P試験)があり、 解説では考え方として ●メンバがフィールドなら「変数の型」 ●インスタンスメソッドなら「実際のオブジェクトの型」 ●クラスメソッドなら「変数の型」 とありました。 できればなぜこのような考え方(法則)になるか理解したいと思っています。 #当方、Javaプログラミング経験ゼロで、実際に下記のようなコーディングをするかどうかもわかりませんが、 #丸暗記だと実際のコーディングで使えそうにないので、できれば理解したいと思ってます。 下記問題は解説の考え方さえ丸暗記すれば解けるのかもしれませんが (なぜ解説のような考え方になるのか含め)教えていただけませんでしょうか。 ----- 【問題1】 class Super{  int d = 10;  void meth()System.out.println(d); } class Sub extends Super{  double d = 20.0;  void meth()System.out.println(d); } class Sample1{  public static void main(String[] args){  Super s = new Sub();  System.out.println(s.d);  s.meth(); } 【答え】 10 20.0 ----- 【問題2】 class Super{  static int d = 10;  static void meth()System.out.println(d); } class Sub extends Super{  static double d = 20.0;  static void meth()System.out.println(d); } class Sample2{  public static void main(String[] args){  Super s = new Sub();  System.out.println(s.d);  s.meth(); } 【答え】 10 10 ----- よろしくお願い致します。

    • ベストアンサー
    • Java
  • デッドロックに関して1

    下記のサンプルソースは、デッドロックを起こすソース なのですが、 public class Deadlock extends Thread { public static Object l1 = new Object(); public static Object l2 = new Object(); private int index; public static void main(String[] a) { Thread t1 = new ThreadA(); Thread t2 = new ThreadB(); t1.start(); t2.start(); } private static class ThreadA extends Thread { public void run() { synchronized (l1) { System.out.println("スレッド1: l1に対するロックを取得"); try { Thread.sleep(10); } catch (InterruptedException e) {} System.out.println("スレッド1: l2のロックの開放待ち"); synchronized (l2) { System.out.println("スレッド1: l1、l2に対するロックを取得"); } } } } private static class ThreadB extends Thread { 文字数オーばーのため削除 } これを参考に、Junitでデッドロックを起こすサンプルを作りたいです。 MultithreadedTestCaseと ControllableTestThreadを使い サンプルを作りたいのですが、 英語が良くわからず苦戦しております。 なにか、よいアドバイスをいただけませんでしょうか? よろしくお願いします

    • ベストアンサー
    • Java
  • 大学数学について

    行列式についての問題です。б=(上段が12345下段が14325)の2と4の互換はわかるのですが、なぜб=(2 4)なのですか?(4 2)ではダメなのですか?あと2つの違いは何なのですか?解説お願いします(T-T)