web-dev-qa-db-ja.com

Java Generics:List、List <Object>、List <?>

誰かが、以下のタイプの違いを可能な限り詳細に説明できますか?

List
List<Object>
List<?>

これをより具体的にさせてください。いつ使いたいですか

// 1 
public void CanYouGiveMeAnAnswer(List l) { }

// 2
public void CanYouGiveMeAnAnswer(List<Object> l) { }

// 3
public void CanYouGiveMeAnAnswer(List<?> l) { }
70
ForYourOwnGood

他の投稿で指摘したように、あなたはJava機能について尋ねています。C++では、これはテンプレートと呼ばれます。Java C++で見つかったものよりも動作します。

機能的にあなたの質問に答えさせてください(それがOO議論のためのいたずらなWordでない場合)。

ジェネリックの前には、Vectorのような具体的なクラスがありました。

_Vector V = new Vector();
_

ベクトルは、与えたオブジェクトを保持します。

_V.add("This is an element");
V.add(new Integer(2));
v.add(new Hashtable());
_

これは、与えられたすべての値をオブジェクト(すべてのJavaクラス)のルート)にキャストすることによりこれを行います。 originalクラスに入れます(意味のある何かをしたい場合)。

_String s = (String) v.get(0);
Integer i = (Integer) v.get(1);
Hashtable h = (Hashtable) v.get(2);
_

キャストは早く古くなります。それ以上に、コンパイラは未チェックのキャストについて不平を言っています。このようなキャストの最も緊急な問題は、正しくキャストするために、Vectorのコンシューマーがcompile timeでその値のクラスを知る必要があることです。ベクターのプロデューサーとそのコンシューマーが完全に分離されている場合(RPCメッセージを考えてください)、これは致命的な問題になる可能性があります。

ジェネリックを入力します。ジェネリックは、ジェネリック操作を行うために強く型付けされたクラスを作成しようとします。

_ArrayList<String> aList = new ArrayList<String>();
aList.add("One");
String element = aList.get(0); // no cast needed
System.out.println("Got one: " + element); 
_

Design Patterns本は、具体的なタイプではなく、契約の観点から考えることを読者に勧めています。実装クラスから変数を離すことには知恵(およびコードの再利用)があります。

これを念頭に置いて、すべての実装Listオブジェクトは同じことを行う必要があると考えるかもしれません:add()get()size()など。リフレクションでは、さまざまな方法でListコントラクトに従うList操作の多くの実装を想像できます(例:ArrayList)。ただし、これらのオブジェクトが扱うデータのタイプは、それらに対して実行されるアクションに直交します。

すべてをまとめると、次の種類のコードが頻繁に表示されます。

_List<String> L = new ArrayList<String>();
_

「Lは、Stringオブジェクトを扱う一種のリストです」と読んでください。 Factoryクラスの処理を開始するとき、特定の実装ではなくコントラクトを処理することが重要です。ファクトリは、実行時にさまざまなタイプのオブジェクトを生成します。

ジェネリックの使用は非常に簡単です(ほとんどの場合)。

ある日、独自のジェネリッククラスを実装したいと思うかもしれません。おそらく、さまざまなデータストア間の違いを排除する新しいデータベース抽象化インターフェイスを作成する必要があります。そのジェネリッククラスを定義するときは、メソッドによって操作されるオブジェクトの種類のプレースホルダーとして_<t>_を使用します。

まだ混乱している場合は、快適になるまでListの汎用クラスを使用してください。後で、少し自信を持って実装に飛び込むことができます。または、JREに同梱されているさまざまなリストクラスのソースコードを確認できます。オープンソースはそのように素晴らしいです。

Oracle/Sunをご覧ください ジェネリックに関するドキュメント 。乾杯。

102
jjohn

私自身の簡単な言葉で:

リスト

通常のコレクションを宣言し、任意の型を保持でき、常にObjectを返します。

リスト<オブジェクト>

任意のタイプのオブジェクトを保持できるリストを作成しますが、別のオブジェクトのみを割り当てることができますList <Object>

たとえば、これは機能しません。

List<Object> l = new ArrayList<String>();

もちろん、何でも追加できますが、オブジェクトをプルすることしかできません。

List<Object> l = new ArrayList<Object>();

l.add( new Employee() );
l.add( new String() );

Object o = l.get( 0 );
Object o2 = l.get( 1 );

最後に

リスト<?>

を含む任意のタイプを割り当てることができます

List <?> l = new ArrayList(); 
List <?> l2 = new ArrayList<String>();

これはnknownのコレクションと呼ばれ、nknownの共通分母はObjectであるため、オブジェクトを取得できます(偶然)

nknownの重要性は、サブクラス化で使用される場合に発生します。

List<? extends Collection> l = new ArrayList<TreeSet>(); // compiles

List<? extends Collection> l = new ArrayList<String>(); // doesn't,
// because String is not part of *Collection* inheritance tree. 

Typeが混乱を引き起こさないようにCollectionを使用することを願っています。それが私の頭に浮かんだ唯一のツリーでした。

ここでの違いは、lはnknowのコレクションであり、これはCollection階層に属していることです。

40
OscarRyz

すでに良い答えをここに追加するには:

メソッドの引数:

List<? extends Foo>

リストを変更するつもりがなく、リスト内のすべてが「Foo」を入力できることだけに注意する場合に適しています。これにより、呼び出し元はList <FooSubclass>を渡すことができ、メソッドが機能します。通常は最良の選択です。

List<Foo>

メソッドのリストにFooオブジェクトを追加する場合に適しています。 Fooをリストに追加するため、呼び出し元はList <FooSubclass>を渡さない場合があります。

List<? super Foo>

リストにFooオブジェクトを追加する場合は良い選択であり、リストに他に何があるかは重要ではありません(つまり、Fooとは関係のない「犬」を含むList <Object>を取得しても大丈夫です)。

メソッドの戻り値

メソッドの引数と同じですが、利点は逆になります。

List<? extends Foo>

返されたリストのすべてのタイプが「Foo」であることを保証します。ただし、List <FooSubclass>の場合があります。発信者はリストに追加できません。これはあなたの頼りになる選択であり、最も一般的なケースです。

List<Foo>

List <のように? Foo>を拡張しますが、呼び出し元がリストに追加できるようにします。あまり一般的ではありません。

List<? super Foo>

呼び出し元がFooオブジェクトをリストに追加することを許可しますが、list.get(0)から返されるものを保証しません... FooからObjectのいずれでもかまいません。唯一の保証は、これが 'Dog'のリストまたはlist.add(foo)の正当性を妨げる他の選択肢ではないことです。非常にまれなユースケース。

それがお役に立てば幸いです。幸運を!

追伸まとめると... 2つの質問...

リストに追加する必要がありますか?リストに何があるか気にしますか?

yes yes-List <Foo>を使用します。

はいいいえ-List <?を使用しますスーパーフー>。

いいえはい-<?を使用しますFooを拡張します----最も一般的です。

no no-<?>を使用します。

15
Eric Lindauer

優れた Java Genericsチュートリアル および "advanced" Genericsチュートリアル を参照してください。どちらもSun Microsystemsから入手できます。もう1つの優れたリソースは、 Java Generics and Collections 本です。

15
Rob

これに詳しく答えようと思います。ジェネリックの前は、List(生のリスト)しかなく、考えられるほとんどすべてのものを保持できました。

List rawList = new ArrayList();
rawList.add("String Item");
rawList.add(new Car("VW"));
rawList.add(new Runnable() {
            @Override
            public void run() {
               // do some work.
            }
        });

生のリストの主な問題は、そのようなリストから要素を取得したい場合、それがObjectであることを保証できるだけであるため、次のようにキャストを使用する必要があります。

   Object item = rawList.get(0); // we get object without casting.
   String sameItem = (String) rawList.get(0); // we can use casting which may fail at runtime.

したがって、結論はListはオブジェクトを格納でき(ほとんどすべてがJavaのオブジェクトです)、常にオブジェクトを返します。

ジェネリック

次にジェネリックについて説明しましょう。次の例を考えてみましょう。

List<String> stringsList = new ArrayList<>();
stringsList.add("Apple");
stringsList.add("Ball");
stringsList.add(new Car("Fiat")); //error
String stringItem = stringsList.get(0);

上記の場合、StringstringsList以外を挿入することはできませんJavaコンパイラーはジェネリックコードに強力な型チェックを適用し、コードが型に違反するとエラーを発行します安全性Carインスタンスを挿入しようとするとエラーが発生します。また、invoke getメソッドをチェックするときに確認できるため、キャストを排除します。理解するにはこのリンクを確認してください ジェネリックを使用する理由

List<Object>

型の消去について読むと、List<String>, List<Long>, List<Animal>などはコンパイル時に異なる静的型を持つことになりますが、実行時には同じ動的型Listを持つことになります。

List<Object>がある場合、Objectのみを格納でき、JavaではほとんどすべてがObjectです。したがって、次のことができます。

 List<Object> objectList = new ArrayList<Object>();
 objectList.add("String Item");
 objectList.add(new Car("VW"));
 objectList.add(new Runnable() {
        @Override
        public void run() {

        }
 });
 Object item = objectList.get(0); // we get object without casting as list contains Object
 String sameItem = (String) objectList.get(0); // we can use casting which may fail at runtime.

List<Object>Listは同じように見えますが、実際は同じではありません。次の場合を考えます。

List<String> tempStringList = new ArrayList<>();
rawList = tempStringList; // Ok as we can assign any list to raw list.
objectList = tempStringList; // error as List<String> is not subtype of List<Obejct> becuase generics are not convariant.

任意のリストを未加工リストに割り当てることができ、その主な理由は後方互換性を許可することです。また、List<String>は、型の消去により実行時にListに変換され、いずれにしても割り当ては問題ありません。

ただし、List<Object>は、オブジェクトのリストのみを参照でき、オブジェクトのみを格納できることを意味します。 StringObjectのサブタイプですが、ジェネリックは配列のような共変ではないため、List<String>List<Object>に割り当てることはできません。それらは不変です。また、これを確認してください link また、この questionListList<Object>の違いも確認してください。

List<?>

これでList<?>が残ります。これは基本的に不明なタイプのリストを意味し、任意のリストを参照できます。

List<?> crazyList = new ArrayList<String>();
 List<String> stringsList = new ArrayList<>();
 stringsList.add("Apple");
 stringsList.add("Ball");
 crazyList = stringsList; // fine

文字?はワイルドカードと呼ばれ、List<?>は無制限のワイルドカードのリストです。今注目すべき点がいくつかあります。

次のコードはコンパイルされないため、このリストをインスタンス化することはできません。

List<?> crazyList = new ArrayList<?>(); // any list.

ワイルドカードのパラメーター化された型は、それ自体ではなく互換性のある型のオブジェクトを参照するために使用できるため、インターフェイス型に似ていると言えます。

List<?> crazyList2 = new ArrayList<String>();

実際にタイプが何であるかわからないため、アイテムを挿入することはできません。

crazyList2.add("Apple"); // error as you dont actually know what is that type.

今、疑問が生じますいつList<?>を使いたいですか?

これは、アイテムの種類を気にしない読み取り専用リストと考えることができます。これを使用して、リストの長さを返す、印刷するなどのメソッドを呼び出すことができます。

 public static void print(List<?> list){
        System.out.println(list);
    }

List, List<?>, List<T>, List<E>, and List<Object>here の違いも確認できます。

4
i_am_zero

「RTFM」ではない最も簡単な説明:

List

多くのコンパイラ警告を生成しますが、ほとんどと同等です:

List<Object>

一方:

List<?>

基本的には一般的なものを意味しますが、一般的なタイプが何であるかはわかりません。 Listを返したばかりの他の戻り値の型を変更できない場合、コンパイラの警告を取り除くのに最適です。その形式ではるかに便利です:

List<? extends SomeOtherThing>
4
John Gardner

最短の説明は次のとおりです。2番目の項目は、任意のタイプを保持できるリストであり、それにオブジェクトを追加できます。

List<Object>

リストする最初の項目は、基本的にこれと同等に扱われますが、「生の型」であるためコンパイラの警告が表示されます。

List

3番目は、任意のタイプを保持できるリストですが、追加することはできません。

List<?> 

基本的に、2番目の形式(List<Object>)任意のオブジェクトを含むことができるリストが本当にあり、リストに要素を追加できるようにしたい場合。 3番目の形式(List<?>)リストをメソッドの戻り値として受け取り、リストを繰り返し処理するが、何も追加しない場合Java 5の下でコンパイルする新しいコードで最初の形式(List)を使用しないでください。またはそれ以降。

3
Eddie

私はこれをこう言いました:ListList<Object>には任意のタイプのオブジェクトを含めることができます、List<?>には未知のタイプの要素が含まれていますが、そのタイプがキャプチャされると、そのタイプの要素のみを含むことができます。これが、これら3つの型の唯一のタイプセーフなバリアントであり、したがって一般的に好ましい理由です。

1
Fabian Steeg

ロブが言及したチュートリアルを補完するために、トピックを説明するウィキブックがあります。
http://en.wikibooks.org/wiki/Java_Programming/Generics


編集:

  1. リスト内のアイテムの種類に制限はありません

  2. リスト内のアイテムはオブジェクトを拡張する必要があります

  3. ワイルドカードは単独で使用されるため、すべてに一致します

この時点で違いがほとんどない/まったくないと結論付けるのは私にとって素朴なことでしょうか?

0
Tim

List, List<?>, and List<? extends Object>は同じものです。 2番目はより明示的です。このタイプのリストについては、どのタイプがそれに入れるのが合法であるかを知ることができず、オブジェクトになることを除いて、そこから取り出すことができるタイプについては何も知りません。

List<Object>は、リストにあらゆる種類のオブジェクトが含まれることを具体的に意味します。

Fooのリストを作成するとしましょう:

List<Foo> foos= new ArrayList<Foo>();

Barをfoosに入れることは違法です。

foos.add(new Bar()); // NOT OK!

List<Object>に何かを入れることは常に合法です。

List<Object> objs = new ArrayList<Object>();
objs.add(new Foo());
objs.add(new Bar());

ただし、BarList<Foo>に入れることを許可しないでください。これがポイントです。つまり、これは次のことを意味します。

List<Object> objs = foos; // NOT OK!

違法です。

しかし、foosは何かのリストであると言っても構いませんが、具体的にはそれが何であるかはわかりません。

List<?> dontKnows = foos;

しかし、それは行くことを禁止しなければならないことを意味します

dontKnows.add(new Foo()); // NOT OK
dontKnows.add(new Bar()); // NOT OK

変数dontKnowsは、どのタイプが有効であるかを知らないためです。

0
PaulMurrayCbr

いつ使いたいか

public void CanYouGiveMeAnAnswer( List l ){}

自分のキャストをすべて行うことができないとき。

いつ使いたいか

public void CanYouGiveMeAnAnswer( List l<Object> ){}

リストのタイプを制限する場合。たとえば、これは無効な引数になります。

 new ArrayList<String>();

いつ使いたいか

public void CanYouGiveMeAnAnswer( List l<?> ){}

ほとんどありません。

0
OscarRyz

リスト<オブジェクト>は、オブジェクトの入力型パラメーターを渡すためのものです。リスト<< >はワイルドカードタイプを表します。ワイルドカード<? >は不明なパラメータータイプです。ワイルドカードは、ジェネリックメソッドの型引数として使用することはできず、クラスのジェネリックインスタンスを作成することもできません。ワイルドカードを使用して、サブタイプクラスList <? Numberを拡張します。オブジェクトタイプの制限を緩和し、この場合は「数値」オブジェクトタイプを緩和します。

0
Juniar