Список против Списка<Объект>

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

РЕДАКТИРОВАТЬ: я обнаружил, что следующее дает ошибку компиляции

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

Зачем?

5 ответов

Решение

Почему мы теряем безопасность типов при использовании 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 без параметров универсального типа.

РЕДАКТИРОВАТЬ:

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

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, и добавляет значение в список.

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

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

Так что нет, это не одно и то же.

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

Поскольку код читается больше, чем пишется, подсказки о намерениях кода могут быть очень ценными в дальнейшем.

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

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

Тип Erasure - это один из ответов и обратная совместимость с до Java 1.5 и более строгой проверкой типов в случае первого.

Обобщения были введены в язык Java для обеспечения более строгой проверки типов во время компиляции и для поддержки универсального программирования. Для реализации универсальных шаблонов компилятор Java применяет стирание типов к:

Замените все параметры типа в универсальных типах их границами или Object, если параметры типа не ограничены. Таким образом, полученный байт-код содержит только обычные классы, интерфейсы и методы. При необходимости вставьте типовые отливки для сохранения типовой безопасности. Сгенерируйте мостовые методы для сохранения полиморфизма в расширенных универсальных типах. Стирание типа гарантирует, что для параметризованных типов не будут созданы новые классы; следовательно, универсальные шаблоны не несут дополнительных затрат времени выполнения.

Другие вопросы по тегам