web-dev-qa-db-ja.com

List <String>がList <Object>として受け入れられないのはなぜですか?

以下に、_List<Object>_をパラメーターとして受け入れるメソッドdoSomething(List<Object>)を考えます。

_private void doSomething(List<Object> list) {
    // do something
}
_

次に、doSomething()を呼び出そうとする以下のコードスニペットを考えます。ここで、_List<String>_をdoSomething()に渡そうとします。

_List<Object> objectList;
List<String> stringList;

doSomething(stringList); // compilation error incompatible types
doSomething(objectList); // works fine 
_

以下のコードでもコンパイルエラーが発生します

_objectList = stringList;  // compilation error incompatible types
_

私の質問は、なぜ_List<String>_が_List<Object>_を受け入れるメソッドに渡されないのかということです。

31
user3374518

Java=のこの一般的な質問は、一見するとStringがオブジェクトのように見えるため、List<String>List<Object>は必須ですが、これは正しくありません。コンパイルエラーが発生します。

List<Object>StringIntegerなどを含むすべてのものを格納できるため、List<String>Stringsのみを保存できます。

また見てください: なぜList <T>から継承しないのですか?

10
Shishir Kumar

StringObjectを拡張しますが、List<String>List<Object>を拡張しません

更新:
一般的に、FooBarのサブタイプ(サブクラスまたはサブインターフェース)であり、Gがいくつかのジェネリック型宣言である場合、それは当てはまりませんG<Foo>G<Bar>のサブタイプです。

これは、コレクションが変更されるためです。あなたの場合、List<String>List<Object>のサブタイプである場合、次のように、スーパータイプを使用してリストが参照されるときに、String以外のタイプを追加できます。

List<String> stringList = new ArrayList<String>;
List<Object> objectList = stringList;// this does compile only if List<String> where subtypes of List<Object>
objectList.add(new Object());
String s = stringList.get(0);// attempt to assign an Object to a String :O

Javaコンパイラはこれらのケースを防ぐ必要があります。

this Javaチュートリアルページの詳細。

43
Ahmad Y. Saleh

間違ったタイプのオブジェクトをリストに入れることができます[〜#〜] if [〜#〜]これはうまくいきました:

private void doSomething(List<Object> list) {
    list.add(new Integer(123)); // Should be fine, it's an object
}

List<String> stringList = new ArrayList<String>();
doSomething(stringList); // If this worked....
String s = stringList.get(0); // ... you'd receive a ClassCastException here
17
Marco13

これらの制限の理由は、分散の考慮に関係しています。

次のコードを見てください。

_public void doSomething(List<Object> objects)
{
  objects.add(new Object());
}
_

あなたの例を拡張すると、次のことを試すことができます:

_List<String> strings = new ArrayList<String>();
string.add("S1");

doSomething(strings);

for (String s : strings)
{
  System.out.println(s.length);
}
_

うまくいけば、コンパイラがこのコードのコンパイルを許可した場合(そうしない場合)にこれが失敗する理由は明らかです。ClassCastExceptionObjectにキャストしようとすると、リストの2番目の項目でStringが発生します。

一般化されたコレクション型を渡すことができるようにするには、これを行う必要があります:

_public void doSomething(List<?> objects)
{
  for (Object obj : objects)
  {
    System.out.println(obj.toString);
  }
}
_

繰り返しますが、コンパイラはあなたの背中を見守っていて、_System.out_をobjects.add(new Object())で置き換えようとすると、objectsが_List<String>_として作成された可能性があるため、コンパイラはこれを許可しません。

分散の詳細については、Wikipediaのアーティカルを参照してください 共分散と反分散

4
Nick Holt

ObjectStringのスーパータイプであるため、List<Object>List<String>のスーパータイプであると予想される場合があります。

この期待は、配列に対してこのような型の関係が存在するという事実から生じています。

ObjectStringのスーパータイプであるため、Object[]String[]のスーパータイプです。 (このタイプの関係は共分散と呼ばれます。)

コンポーネントタイプのスーパーサブタイプの関係は、対応する配列タイプに拡張されます。

ジェネリック型のインスタンス化には、そのような型の関係はありません。 (パラメーター化された型は共変ではありません。)

詳細は こちら を確認してください

3

From Javaジェネリックのチュートリアル:

ジェネリックの理解度をテストしてみましょう。次のコードスニペットは合法ですか?

List<String> ls = new ArrayList<String>(); // 1
List<Object> lo = ls; // 2 

1行目は確かに合法です。質問のトリッキーな部分は2行目です。これは質問に要約されます:オブジェクトのリストである文字列のリストです。ほとんどの人は本能的に「確かに」と答えます。

さて、次の数行を見てください:

lo.add(new Object()); // 3
String s = ls.get(0); // 4: Attempts to assign an Object to a String!

ここでは、lsとloのエイリアスを設定しています。 Stringのリストであるlsにアクセスし、別名loを介して、任意のオブジェクトを挿入できます。その結果、lsは文字列だけを保持しなくなり、そこから何かを取得しようとすると、失礼な驚きが生じます。

Javaコンパイラはこれが発生するのを防ぎます。2行目はコンパイル時エラーを引き起こします。

出典: ジェネリックとサブタイピング

2
Ankit Lamba

どのデータ型を取るかわからない場合は、次のようにJavaでGenericsを使用できます。

public static void doSomething(List<?> data) {

}

public static void main(String [] args) {
    List<Object> objectList = new ArrayList<Object>();
    List<String> stringList = new ArrayList<String>();
    doSomething(objectList);
    doSomething(stringList);
}

ただし、データを使用するときは、適切なデータ型をタイプキャストとして指定する必要があります。

0
rsakhale