Почему можно привести общий класс?
Обобщения Java являются инвариантами, поэтому невозможно выполнить такое приведение:
List<Object> li = (List<Object>)new ArrayList<Integer>();
Но в следующем коде в строке 4 я могу привести List<Integer>
в List<T>
, где T
может быть любого типа. Почему этот тип броска разрешен?
Я знаю, что он генерирует предупреждение о непроверенном приведении, но дело в том, что это приведение возможно внутри параметризованного метода, но не в обычном коде. Учитывая, что генерики являются инвариантами, почему это разрешено? В нормальном коде при наличии List<Integer>
Я могу только привести его к List<Integer>
что не имеет смысла, а другие забросы являются незаконными. Так какой смысл разрешать такое приведение, как в строке 4?
Я знаю, что универсальные типы удаляются во время компиляции, и это заканчивается List xlist = (List)list
, но перед удалением этих типов очевидно, что это приведение не должно быть разрешено, если оно не принимается только для случая, когда кто-то передает Integer как el
что не имеет особого смысла.
class Test {
public static <T> void t(List<Integer> list, T el) {
List<T> xlist = (List<T>)list; //OK
xlist.add(el);
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
t(list, "a");
t(list, "b");
//prints [a, b] even if List type is Integer
System.out.println(list);
}
}
2 ответа
В Java это ошибка компиляции, когда выполняется явное приведение, которое, как известно, во время компиляции всегда неверно или всегда корректно. Актерский состав из List<Integer>
в List<Object>
во время компиляции известно, что она всегда неверна, поэтому не допускается. Актер из List<Integer>
в List<T>
не известно, что во время компиляции всегда будет неправильно - это будет правильно, если T
мы Integer
, а также T
не известно во время компиляции.
Я думаю, ответ заключается в том, что этот акт возможен только потому, что Integer
может быть передан в качестве аргумента el
и в этом случае этот состав будет правильным. Во всех других случаях типов, отличных от Integer, это будет незаконное приведение. Но, как я вижу в дженериках, если есть хотя бы один тип, который дал бы правильное приведение (Integer
) тогда компилятор выдает не ошибку, а предупреждение, даже если все остальные случаи недействительны.
Object - это суперкласс класса Integer. List<Object>
не суперкласс класса List<Integer>
, но, кроме того, если вы хотите List<Object>
сослаться на List<Integer>
вы можете использовать знак джокера (?).
List<?> list1 = new List<Object>();
List<?> list2 = new List<Integer>();
list1 = list2;