Подтвердить что ты не робот

Почему List <String> неприемлем как List <Object>?

Рассмотрим ниже метод doSomething(List<Object>), который принимает 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>?

4b9b3361

Ответ 1

Этот общий вопрос в Java может показаться запутанным для любого, кто не очень хорошо знаком с Generics, так как на первый взгляд выглядит, что String - это объект, поэтому List<String> можно использовать там, где требуется List<Object>, но это неверно. Это приведет к ошибке компиляции.

Это имеет смысл, если вы сделаете еще один шаг, потому что List<Object> может хранить что-либо, включая String, Integer и т.д., но List<String> может хранить только Strings.

Также посмотрите: Почему бы не наследовать из списка <T> ?

Ответ 2

Поскольку while String extends Object, List<String> не распространяется List<Object>

Update:
В общем случае, если Foo является подтипом (подкласс или подинтерфейс) Bar, а G - это объявление общего типа, это не тот случай, когда G<Foo> является подтипом G<Bar>.

Это потому, что коллекции меняются. В вашем случае If 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 должен предотвратить эти случаи.

Подробнее о этом в разделе "Учебное пособие по Java".

Ответ 3

Вы можете поместить объект неправильного типа в список, если это сработало:

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

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

Ответ 4

Причина этих ограничений связана с соображениями дисперсии.

Возьмите следующий код:

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);
}

Надеюсь, это ясно, почему это сломается, если компилятор разрешил компиляцию этого кода (чего нет) - a ClassCastException возникнет для второго элемента в списке при попытке применить Object к 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 art Ковариация и контравариантность

Ответ 5

Иногда ожидается, что a List<Object> будет супертипом a List<String>, потому что Object является супертипом String.

Это ожидание связано с тем, что такое отношение типов существует для массивов:

Object[] является супертипом String[], потому что Object является супертипом String. (Отношение этого типа известно как ковариация.)

Супер-подтип-отношение типов компонентов распространяется на соответствующие типы массивов.

Нет такого отношения типа для экземпляров родовых типов. (Параметрированные типы не ковариантны.)

Подробнее о здесь для более подробной информации

Ответ 6

Из учебных пособий по 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. Получив доступ к ls, список String, через псевдоним lo, мы можем вставить в него произвольные объекты. В результате ls больше не держит Strings, и когда мы пытаемся извлечь что-то из этого, мы получаем грубый сюрприз.

Компилятор Java предотвратит это, конечно. Строка 2 вызовет ошибку времени компиляции.

Источник: Общие и подтипирование

Ответ 7

Если вы не уверены, какой тип данных он примет, вы можете использовать Generics на Java, как показано ниже.

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);
}

Но при использовании данных вам будет необходимо указать правильный тип данных как тип Cast