リスト、リスト<?>、リスト<T>、リスト<E>、リスト<オブジェクト>の違い


194

どのような違いがあるListList<?>List<T>List<E>、とList<Object>

1.リスト

List:は生の型なので、ではありませんtypesafe。キャストが悪い場合にのみランタイムエラーが発生します。キャストが悪いときにコンパイル時エラーが欲しい。使用はお勧めしません。

2.リスト<?>

List<?>無制限のワイルドカードです。しかし、それが何のためにあるのかわかりませんか?List<?>問題なく印刷できます:

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

にアイテムを追加できないのはなぜList<?>ですか?

public static void test(List<?> list){
    list.add(new Long(2));     // Error
    list.add("2");             // Error
    System.out.println(list);
}

3.リスト<T>

public static void test(List<T> list){   // T cannot be resolved
    System.out.println(list);
}

この構文が理解できません。私はこのようなものを見ました、そしてそれはうまくいきます:

public <T> T[] toArray(T[] a){
    return a;   
}

時々 、私は参照<T>、または<E>、または<U><T,E>。それらはすべて同じですか、それとも別のものを表していますか?

4. List <オブジェクト>

これにより、「メソッドtest(List<Object>)は引数に適用できません」というエラーが発生しますList<String>

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

これを試してみると、「からキャストできません」を取得しList<String>ましたList<Object>

test((List<Object>) names);

私は混乱しています。StringのサブクラスなObjectので、なぜList<String>のサブクラスではないのList<Object>ですか?

回答:


77

1)正しい

2)アイテムのタイプを気にしない「読み取り専用」リストと考えることができます。たとえば、リストの長さを返すメソッドで使用できます。

3)T、E、Uは同じですが、たとえばタイプにはT、要素にはE、値にはV、キーにはKを使用する傾向があります。コンパイルするメソッドは、特定のタイプの配列を取り、同じタイプの配列を返すと言っています。

4)オレンジとリンゴを混ぜることはできません。オブジェクトリストが必要なメソッドに文字列リストを渡すことができれば、文字列リストにオブジェクトを追加できます。(すべてのオブジェクトが文字列であるとは限りません)


2
の読み取り専用リストの+1 2。これをデモンストレーションするためのコードをいくつか書いています2。tyvm
Thang Pham、

なぜ人々は使用List<Object>するのですか?
Thang Pham、2011年

3
これは、あらゆるタイプのアイテムを受け入れるリストを作成する方法であり、ほとんど使用しません。
Kaj

実際には、あなたが識別子をまったく持っていないことを考えると、誰もそれを使うとは思いません。
if_zero_equals_one

1
@if_zero_equals_oneはい。ただし、コンパイラの警告が表示され(生の型を使用していると警告され、警告が表示されます)、警告付きでコードをコンパイルする必要はありません。
Kaj

26

最後の部分について:StringはObjectのサブセットですが、List <String>はList <Object>から継承されません。


11
非常に良い点。多くの人は、クラスCがクラスPから継承するため、そのList <C>もList <P>から継承すると想定しています。あなたが指摘したように、これはそうではありません。その理由は、List <String>からList <Object>にキャストできる場合、オブジェクトをそのリストに入れることができるため、要素を取得しようとすると、List <String>の元の規約に違反するためです。
Peter

2
+1。良い点も。では、なぜ人々は使用List<Object>するのでしょうか?
タンファン

9
List <Object>は、さまざまなクラスのオブジェクトのリストを格納するために使用できます。
Farshid Zaker

20

表記List<?>は「何かのリスト(しかし私は何を言っていない)」を意味します。のコードtestはリスト内のあらゆる種類のオブジェクトに対して機能するため、これは正式なメソッドパラメータとして機能します。

(ポイント3のように)型パラメーターを使用するには、型パラメーターを宣言する必要があります。そのためのJava構文<T>は、関数の前に置くことです。これは、メソッド本体で名前を使用する前に、メソッドに仮パラメーター名を宣言するのとまったく同じです。

List<Object>aを受け入れないことに関しては、aが受け入れられないList<String>ため、それは理にかなってStringObjectます。これはのサブクラスですObject。修正は宣言することpublic static void test(List<? extends Object> set) ...です。ただし、extends Objectすべてのクラスが直接的または間接的に拡張されるため、は冗長Objectです。


なぜ人々は使用List<Object>するのですか?
Thang Pham、2011年

10
「何かのリスト」というのは、List<?>特定のタイプですが未知のタイプのリストなので、より良い意味だと思います。List<Object>それは確かに何かを含むことができるので、本当に「何かのリスト」になるでしょう。
ColinD

1
@ColinD-「何でも」という意味で「何でも」を意味しました。しかし、あなたは正しいです。それは、「何かのリストですが、何をあなたに教えるつもりはありません」という意味です。
テッドホップ

@ColinDそれは彼がなぜあなたが彼の言葉を繰り返すのを意味したのですか?はい、少し違う単語で書かれていますが、意味は同じです...
user25

14

あなたはキャストすることができない理由List<String>にはList<Object>、それはあなたがの制約に違反することが可能になるということですList<String>

次のシナリオについて考えてみます。私がを持っている場合List<String>、にはタイプのオブジェクトのみが含まれているはずStringです。(これはfinalクラスです)

これをにキャストできる場合、そのリストList<Object>に追加できるObjectため、の元の規約に違反しList<String>ます。

したがって、一般に、クラスがクラスCから継承する場合P、それがGenericType<C>からも継承するとは言えませんGenericType<P>

NB私は以前の回答ですでにこれについてコメントしましたが、それについてさらに詳しく説明したいと思いました。


tyvm、私はあなたのコメントとあなたの答えの両方をアップロードします。それは非常に良い説明だからです。今、どこで、なぜ人々は使うのList<Object>でしょうか?
Thang Pham、2011年

3
一般的にList<Object>、ジェネリックの目的に反するため、使用しないでください。ただし、古いコードがList異なる型を受け入れる場合があるので、生の型に関するコンパイラの警告を回避するためだけに型のパラメータ化を使用するようにコードを改良することができます。(ただし、機能は変更されていません)
Peter


5

Javaの歴史の文脈でそれらについて話しましょう。

  1. List

リストは、任意のオブジェクトを含めることができることを意味します。リストはJava 5.0より前のリリースにありました。Java 5.0では、下位互換性のためにListが導入されました。

List list=new  ArrayList();
list.add(anyObject);
  1. List<?>

?どのオブジェクトでもない未知のオブジェクトを意味します。ワイルドカードの?導入は、ジェネリック型によって構築された問題を解決するためのものです。ワイルドカードを参照してください。しかし、これはまた別の問題を引き起こします:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
  1. List< T> List< E>

プロジェクトLibにT型またはE型がないことを前提とした一般的な宣言を意味します。

  1. List< Object> 一般的なパラメータ化を意味します。

5

3番目の点では、「T」は宣言されていないため解決できません。通常、ジェネリッククラスを宣言すると、バインドされた型パラメーターの名前として「T」を使用できます。Oracleのチュートリアルを含む多くのオンライン例では、「T」をタイプパラメータの名前。たとえば、次のようなクラスを宣言するとします。

public class FooHandler<T>
{
   public void operateOnFoo(T foo) { /*some foo handling code here*/}

}

FooHandler's operateOnFooメソッドは、クラス宣言自体で宣言されている「T」型の変数を期待していることを言っています。これを念頭に置いて、後で別のメソッドを追加できます

public void operateOnFoos(List<T> foos)

T、E、またはUのすべてのケースで、型パラメーターのすべての識別子があります。構文を使用する複数の型パラメーターを持つこともできます。

public class MyClass<Atype,AnotherType> {}

StingはObjectのサブタイプですが、ジェネリッククラスにはそのような関係はList<String>ありませんが、サブタイプではありませんList<Object>が、コンパイラの観点からは2つの異なるタイプです。これは、このブログエントリで最もよく説明されています。


5

理論

String[] にキャストすることができます Object[]

だが

List<String>にキャストできませんList<Object>

練習

リストの場合は、コンパイル時にメソッドに渡されるListパラメーターの型がチェックされないため、それよりも微妙です。メソッドの定義はList<?>、コンパイラの観点からは同等と言えるかもしれません。これが、OPの例2でコンパイルエラーではなく実行時エラーが発生する理由です。

List<Object>メソッドに渡されたパラメーターを慎重に処理して、リストの要素の型チェックを強制しない場合は、メソッドを使用してメソッドを定義できますList<Object>が、実際にList<String>は呼び出し元のコードからパラメーターを受け入れます。

A. したがって、このコードはコンパイルエラーや実行時エラーを発生させず、実際に(そしておそらく驚くべきことに)機能します。

public static void main(String[] args) {
    List argsList = new ArrayList<String>();
    argsList.addAll(Arrays.asList(args));
    test(argsList);  // The object passed here is a List<String>
}

public static void test(List<Object> set) {
    List<Object> params = new ArrayList<>();  // This is a List<Object>
    params.addAll(set);       // Each String in set can be added to List<Object>
    params.add(new Long(2));  // A Long can be added to List<Object>
    System.out.println(params);
}

B. このコードは実行時エラーを発生させます:

public static void main(String[] args) {
    List argsList = new ArrayList<String>();
    argsList.addAll(Arrays.asList(args));
    test1(argsList);
    test2(argsList);
}

public static void test1(List<Object> set) {
    List<Object> params = set;  // Surprise!  Runtime error
}

public static void test2(List<Object> set) {
    set.add(new Long(2));       // Also a runtime error
}

C. このコードは実行時エラー(java.lang.ArrayStoreException: java.util.Collections$UnmodifiableRandomAccessList Object[])を出します:

public static void main(String[] args) {
    test(args);
}

public static void test(Object[] set) {
    Object[] params = set;    // This is OK even at runtime
    params[0] = new Long(2);  // Surprise!  Runtime error
}

Bでは、パラメーターsetListコンパイル時に型指定されませんList<?>。コンパイラーはそれをと認識します。実行時に、ので、ランタイムエラーがありますsetから渡された実際のオブジェクトとなりmain()、それがありますList<String>。をList<String>にキャストすることはできませんList<Object>

Cでは、パラメーターにsetはが必要Object[]です。String[]オブジェクトをパラメーターとして呼び出した場合、コンパイルエラーやランタイムエラーは発生しません。だString[]キャストへObject[]。しかし、が受け取った実際のオブジェクトはのtest()ままString[]です。変更されませんでした。したがって、paramsオブジェクトもになりますString[]。そして、の要素0 String[]に割り当てることはできませんLong

(うまくいけば、ここにすべてが揃っていて、私の推論が間違っている場合は、コミュニティが教えてくれると確信しています。


私はあなたの例を試しましたAそれは機能しませんList<Object> cannot be applied to List<String>。を期待するメソッドに渡すことはできませんArrayList<String>ArrayList<Object>
パーサー

おかげで、日付がかなり遅くなったので、例Aを調整して動作するようにしました。主な変更は、argsListを一般的にmain()で定義することでした。
-radfast

4

「System.out.println(set);」なので、問題2は問題ありません。「System.out.println(set.toString());」を意味します setはListのインスタンスなので、コンパイラはList.toString();を呼び出します。

public static void test(List<?> set){
set.add(new Long(2)); //--> Error  
set.add("2");    //--> Error
System.out.println(set);
} 
Element ? will not promise Long and String, so complier will  not accept Long and String Object

public static void test(List<String> set){
set.add(new Long(2)); //--> Error
set.add("2");    //--> Work
System.out.println(set);
}
Element String promise it a String, so complier will accept String Object

問題3:これらの記号は同じですが、異なる仕様を指定できます。例えば:

public <T extends Integer,E extends String> void p(T t, E e) {}

問題4:コレクションは、型パラメーターの共分散を許可しません。しかし、配列は共分散を可能にします。


0

そうです、文字列はオブジェクトのサブセットです。StringはObjectより「正確」であるため、System.out.println()の引数として使用するにはキャストする必要があります。

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