「E」、「T」、「?」の違いは何ですか?Javaジェネリックの場合?


261

私はこのようなJavaコードに出くわします:

public interface Foo<E> {}

public interface Bar<T> {}

public interface Zar<?> {}

上記の3つすべての違いは何ですか?Javaでこのタイプのクラスまたはインターフェース宣言を何と呼びますか?


1
違いがあるとは思えません。それはその型パラメーターの単なる名前だと思います。そして最後のものは有効ですか?
CodesInChaos

回答:


231

最初の2つに違いはありません。タイプパラメータEまたはT)に異なる名前を使用しているだけです。

第三は、有効な宣言されていない- ?として使用されているワイルドカード型提供する際に使用される引数、例えばList<?> foo = ...手段fooいくつかのタイプのリストを参照し、私たちは何を知っていません。

これらはすべてジェネリックです。これはかなり大きなトピックです。もちろん、他にも利用可能なものがありますが、次のリソースを通じてそれについて学びたいと思うかもしれません。


1
PDFへのリンクが壊れているようです。ここではコピーのように見えるものを見つけましたが、元のイメージがわからないので100%確実ではありません。
ジョン

2
@ジョン:そうだね。そのリンクでもOracleのリンクでも、リンクを編集します...
Jon Skeet

T、E、および他に何かありますか?ジェネリックで使用されていますか もしそうなら、それらは何であり、それらはどういう意味ですか?
sofs1 2018

1
@ sofs1は:そこについては何も特別だTE-彼らはただの識別子です。KeyValuePair<K, V>たとえば、次のように書くことができます。?ただし、特別な意味があります。
Jon Skeet、2018

215

それは何よりも慣習です。

  • T タイプであることを意味します
  • E要素であることを意味します(List<E>:要素のリスト)
  • Kキーです(内Map<K,V>
  • V 値(戻り値またはマップされた値として)

それらは完全に交換可能です(それにもかかわらず、同じ宣言での矛盾)。


20
<>の間の文字は単なる名前です。答えで説明するのは単なる慣例です。大文字である必要はありません。クラスや変数などに好きな名前を付けることができるのと同じように、好きな名前を使うことができます。
Jesper、

より詳細で明確な説明は、この記事で提供されていますoracle.com/technetwork/articles/java/...
fgul

6
あなたは疑問符に説明しませんでした。反対投票。
しんぞう

129

これまでの回答では、型パラメーター(T、Eなど)について説明しましたが、ワイルドカード「?」、またはそれらの違いについては説明しないので、ここで説明します。

まず、明確にするために、ワイルドカードとタイプパラメータは同じではありません。型パラメーターがスコープの型を表す変数(Tなど)を定義する場合、ワイルドカードはそうではありません。ワイルドカードは、ジェネリック型に使用できる一連の許容可能な型を定義するだけです。境界(extendsまたはsuper)がない場合、ワイルドカードは「ここで任意のタイプを使用する」ことを意味します。

ワイルドカードは常に山括弧の間にあり、ジェネリック型のコンテキストでのみ意味があります。

public void foo(List<?> listOfAnyType) {...}  // pass a List of any type

決して

public <?> ? bar(? someType) {...}  // error. Must use type params here

または

public class MyGeneric ? {      // error
    public ? getFoo() { ... }   // error
    ...
}

それらが重なり合う場所はさらに混乱します。例えば:

List<T> fooList;  // A list which will be of type T, when T is chosen.
                  // Requires T was defined above in this scope
List<?> barList;  // A list of some type, decided elsewhere. You can do
                  // this anywhere, no T required.

メソッド定義で可能なことには多くの重複があります。以下は、機能的には同じです。

public <T> void foo(List<T> listOfT) {...}
public void bar(List<?> listOfSomething)  {...}

それで、重複がある場合、なぜどちらか一方を使用するのですか?時には、それは正直だけのスタイルです:一部の人々はあなたがいない場合と言う必要があるタイプのparamを、あなただけのより読みやすい/コードを簡単にするためにワイルドカードを使用する必要があります。上記で説明した主な違いの1つ:type paramsは、スコープ内の他の場所で使用できる型変数(Tなど)を定義します。ワイルドカードにはありません。それ以外の場合、タイプparamsとワイルドカードには2つの大きな違いがあります。

タイプparamsは複数の境界クラスを持つことができます。ワイルドカードは次のことはできません。

public class Foo <T extends Comparable<T> & Cloneable> {...}

ワイルドカードには下限を設定できます。タイプparamsは:

public void bar(List<? super Integer> list) {...}

上記では、List<? super Integer>Integerワイルドカードの下限として定義されています。つまり、リストのタイプは整数またはスーパータイプの整数でなければなりません。ジェネリック型の境界は、私が詳細にカバーしたいものを超えています。つまり、ジェネリック型をどの型にすることができるかを定義できます。これにより、ジェネリックを多態的に扱うことができます。例:

public void foo(List<? extends Number> numbers) {...}

あなたは渡すことができList<Integer>List<Float>List<Byte>、などのためにnumbers。型の境界がないと、これは機能しません。

最後に、ワイルドカードを使用して、他の方法では実行できないと思われる処理を行うメソッド定義を次に示します。

public static <T extends Number> void adder(T elem, List<? super Number> numberSuper) {
    numberSuper.add(elem);
}

numberSuper番号の一覧または番号(例えば、任意のスーパータイプすることができList<Object>)、およびelem番号または任意のサブタイプでなければなりません。すべての境界があるため、コンパイラー.add()は型保証であると確信できます。


「public void foo(List <?extends Number> numbers){...}」は、「extends」が「super」である必要がありますか?
1a1a11a 2015

1
いいえ。その例の要点は、NumberのリストとNumberのサブタイプを多態的にサポートする署名を示すことです。これには、「拡張」を使用します。つまり、「List of Number または Numberを拡張するものをすべて渡します」(List <Integer>、List <Float>など)。このようなメソッドは、リストを反復処理し、各要素「e」に対して、たとえばe.floatValue()を実行します。渡すNumberのサブタイプ(拡張子)は関係ありません-.floatValue()はNumberのメソッドであるため、常に ".floatValue()"を使用できます。
Hawkeye Parker

最後の例では、メソッドがより一般的なものを許可しないため、「List <?super Number>」は単に「List <Number>」になります。
jessarah

@jessarahいいえ。多分私の例は不明瞭ですが、例ではadder()がList <Object>を取ることができることを述べました(オブジェクトはNumberのスーパークラスです)。これを実行できるようにする場合は、 "List <?super Number>"という署名が必要です。ここがまさに「スーパー」のポイントです。
Hawkeye Parker

2
この回答は、ワイルドカードとタイプパラメータの違いを説明するのに非常に適しています。この回答には1つの専用の質問があるはずです。私は最近ジェネリックをより深く掘り下げており、この回答は物事をまとめるのに大いに役立ちました、要するに多くの正確な情報、ありがとう!
Testo Testini 2017

27

型変数<T>は、任意の非プリミティブ型(クラス型、インターフェイス型、配列型、または別の型変数)にすることができます。

最も一般的に使用されるタイプパラメータ名は次のとおりです。

  • E-要素(Javaコレクションフレームワークで広く使用されています)
  • K-キー
  • N-番号
  • T-タイプ
  • V-値

Java 7では、次のようにインスタンス化できます。

Foo<String, Integer> foo = new Foo<>(); // Java 7
Foo<String, Integer> foo = new Foo<String, Integer>(); // Java 6

3

最も一般的に使用されるタイプパラメータ名は次のとおりです。

E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types

これらの名前は、Java SE API全体で使用されます。


2

コンパイラーは、次のような関数を構成するときに、ワイルドカード(リスト内の疑問符など)ごとにキャプチャを作成します。

foo(List<?> list) {
    list.put(list.get()) // ERROR: capture and Object are not identical type.
}

ただし、Vのようなジェネリック型は問題なく、ジェネリックメソッドにすることができます。

<V>void foo(List<V> list) {
    list.put(list.get())
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.