みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

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

class TestTest<T extends Number> {
ㅤList<? extends Number> list;
}

これは問題ないのに

class TestTest<? extends Number> {
ㅤList<T extends Number> list;
}

これはダメなのはなぜですか。
猿にも分かる解説がほしいです。

投稿日時 - 2017-10-20 16:53:42

QNo.9387970

すぐに回答ほしいです

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

>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>」はエラーにならない。

投稿日時 - 2017-10-20 22:38:33

お礼

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

投稿日時 - 2017-10-27 11:00:18

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(6)

ANo.6

これは「オブジェクト変数の生成」に関する説明で、インスタンスの生成とは違います。

>クラスで仮型パラメータ「T」が設定されているなら、コンパイル時に「T」は実型パラメータとなるので「<T>」はエラーにならない。

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

投稿日時 - 2017-10-24 11:02:14

ANo.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」の下位クラスでなければならないとか、制限はありますが。

投稿日時 - 2017-10-24 09:01:14

ANo.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;
です。

投稿日時 - 2017-10-23 15:41:03

ANo.3

>class TestTest<T super Number>{

(classなどでの)「型変数の宣言」では「<T super Number>」などのように「super」は使えないようです。

ちなみに「class」と同様に「interface」も「型変数の宣言」が可能なようです。
「interface」のジェネリクスの制限も「class」と同様の制限のようです。

投稿日時 - 2017-10-22 12:44:52

ANo.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>」はエラーになる。

投稿日時 - 2017-10-21 09:34:10