Список 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 не может быть проверена. Это действительно предупреждение о компиляторе здесь - это говорит о том, что это не может гарантировать безопасность типов дженериков, и это не произойдет во время выполнения (пока в какой-то более поздний момент не будет фактического нажатия в коде).