Список vs Список <Объект>

Почему мы теряем безопасность типа при использовании List, а не при использовании List<Object>? Разве это не одно и то же?

EDIT: я обнаружил, что следующее дает ошибку компиляции

public class TestClass
{
    static void func(List<Object> o, Object s){
        o.add(s);
    }

    public static void main(String[] args){
        func(new ArrayList<String>(), new Integer(1));
    }
}

тогда как это не

public class TestClass
{
    static void func(List o, Object s){
        o.add(s);
    }

    public static void main(String[] args){
        func(new ArrayList<String>(), new Integer(1));
    }
}

Почему?

Ответ 1

Почему мы теряем безопасность типа при использовании List, а не при использовании List<Object>? Разве это не одно и то же?

Нет, это не одно и то же.

Если вы предоставляете API,

class API {
  static List<Object> getList() { ... }
  static void modifyList(List<Object> l) { ... }
}

и клиент использует его неправильно

List<Integer> list = API.getList();
API.modifyList(list);
for (Integer i : list) { ... }  // Invalid

тогда, когда ваш API указывает List<Object>, они получают ошибку времени компиляции, но при этом API.getList() не возвращает List и API.modifyList(list) принимает List без параметров типового типа.

EDIT:

В комментариях вы упомянули изменение

void func(List<Object> s, Object c) { s.add(c); }

к

void func(List s, Object c) { s.add(c); }

так что

func(new List<String>(), "");

будет работать.

Это нарушает безопасность типа. Безопасный способ для этого -

<T> void func(List<? super T> s, T c) { s.add(c); }

который в основном говорит о том, что func является параметризованной функцией, которая принимает List, тип которой может быть любым супер классом T и значением типа T, и добавляет значение в список.

Ответ 2

A List<Object> на самом деле не является более типичным, чем a List. Однако Object в коде подразумевает намерение. Когда кто-то еще смотрит на него позже, они могут видеть, что вы целенаправленно выбрали Object как тип, вместо того, чтобы задаваться вопросом, не забыл ли вы просто поместить тип или хранить что-то еще и приписывать его в другом месте.

Так как код читается больше, чем записывается, подсказки с целью кода могут быть очень полезны позже.

Ответ 3

List - это список какого-либо типа, которого вы не знаете. Это может быть List<String>, List<Integer> и т.д.
Он фактически эквивалентен List<?> или List<? extends Object>, за исключением того, что он не документирует этот факт. Он поддерживается только для обратной совместимости.

List<Object> - это список объектов. Любой объект любого типа может быть помещен внутри него, в отличие от List<String>, например, который принимает только строки.

Нет, они не то же самое.

Ответ 4

Для целей здесь вы можете сказать, что они одно и то же. Но предположительно вы почти никогда не заполняете List чистым экземпляром Object. Они String или что-то в этом роде. В этом примере List<Object> технически использует дженерики, но на самом деле не использует его. Таким образом, он теряет проверку типов данных компиляции во времени.

Ответ 5

Причина, по которой у вас есть предупреждение о компиляторе при использовании List вместо List<Object>, заключается в том, что когда у вас есть List, компилятор не знает, какой тип списка он есть, поэтому пока вы можете рассматривать его как a List<Object>, компилятор не может гарантировать, что в какой-то другой точке кода он не был установлен для ссылки на List<String>, и безопасность типов Generics не может быть проверена. Это действительно предупреждение о компиляторе здесь - это говорит о том, что это не может гарантировать безопасность типов дженериков, и это не произойдет во время выполнения (пока в какой-то более поздний момент не будет фактического нажатия в коде).