Почему можно привести общий класс?

Обобщения 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;
Другие вопросы по тегам