Не очевидное поведение дженериков

113
04 июня 2021, 20:10

Есть вот такой java класс:

public class SomeType<T> {
    public <E> void test(Collection<E> collection) {
        for (E e : collection) {
            System.out.println(e);
        }
    }
    public void test(List<Integer> list) {
        for (Integer integer : list) {
            System.out.println(integer);
        }
    }
}

Если исполнять код так (создать класс любым из трех вариантов):

    public static void main(String[] args) {
        SomeType someType = new SomeType<>();
        //SomeType someType = new SomeType<String>();
        //SomeType someType = new SomeType();
        List<String> stringList = Arrays.asList("value");
        someType.test(stringList);
    }

то выбрасывается:

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

И только при таком выполнении

    public static void main(String[] args) {
        SomeType<String> someType = new SomeType<>();
        List<String> stringList = Arrays.asList("value");
        someType.test(stringList);
    }

код отрабатывает успешно и на экран выводится value.

Может кто-нибудь объяснить почему именно так работает java? Вроде бы дженерик тип класса T не связан с дженерик типом метода E.

И еще, если класс не дженерик:

public class SomeType {
    public <E> void test(Collection<E> collection) {
        for (E e : collection) {
            System.out.println(e);
        }
    }
    public void test(List<Integer> list) {
        for (Integer integer : list) {
            System.out.println(integer);
        }
    }
}

то такой код отрабатывает нормально

    public static void main(String[] args) {
        SomeType someType = new SomeType();
        List<String> stringList = Arrays.asList("value");
        someType.test(stringList);
    }
Answer 1

в первом варианте, вы создает экземпляр SomeType сырого типа, параметризованные методы экземпляра сырого типа затираются и ваш метод с дженериком Е, после компиляции, превращается в метод вида :

public Object void test(Collection collection) {
    for (Object e : collection) {
        System.out.println(e);
    }
}

при вызове на вход метода подается List<String>, так как обобщенные типы инвариантны, параметр List<String> инвариант для Collection, не расширяет его, поэтому компилятор не связывает вызов с первым, дженерализированным(до компиляции) методом. А вот второй метод, превращается в следующее:

public void test(List list) {
        for (Integer integer : list) {
            System.out.println(integer);
        }
    } 

и может принять на вход лист чего угодно, получает List<String> и пытается прикастовать элемент к Integer.

Для того чтобы ваш код в первом случае правильно работал вы может подать на вход такую конструкцию Collection stringList = Arrays.asList("value");

READ ALSO
Максимальная длина REST запроса в Retrofit

Максимальная длина REST запроса в Retrofit

Есть следующий интерфейс

89
В RecyclerView криво масштабируются изображения

В RecyclerView криво масштабируются изображения

В RecyclerView ,макет которого состоит из текста + изображения некоторые ImageView криво располагаютсяКак я понял, это связано с RecyclerView, т

123
Получить список ребер между двумя вершинами графа

Получить список ребер между двумя вершинами графа

Нужно реализовать метод getPath так чтобы он возвращал список ребер, путь не обязательно должен быть оптимальным, но должно учитываться isDirected...

109
Есть желание написать клиент серверное приложение чат для Android

Есть желание написать клиент серверное приложение чат для Android

Само приложение хотелось бы создавать на Android studioПрошу подсказать с технологиями, которые стоит использовать в создании (напр

214