Почему создание generi c List не связывает нужный тип данных во время выполнения? - PullRequest
0 голосов
/ 29 апреля 2020

Я пытаюсь создать обобщенный c метод, который возвращает обобщенный c список с данным типом данных.

Я передал Integer как тип данных и заполнил String без проблем во время выполнения.

Мой вопрос, почему я не получил ClassCastException во время выполнения?

    public static void main(String[] args) {
        List<Integer> list = (List<Integer>) generic(Integer.class);
        System.out.println(list);
    }

    private static <T> List<?> generic(T clazz) {
        List<T> list = new ArrayList<>();
        list.add((T) Arrays.asList("create", "generic"));
        list.add((T) Arrays.asList("create", "generic2"));
        return list;
    }

, вывод

[[create, generic], [create, generic2]]

Ответы [ 3 ]

1 голос
/ 30 апреля 2020

Из-за стирания типа не все нарушения системы типа generi c выявляются во время выполнения. Эта ситуация также называется куча загрязнений . Но вы должны были получить «непроверенные» предупреждения от компилятора.

Но ваше предположение, что вы создаете List<Integer>, в любом случае неверно. Вы передаете Integer.class в качестве аргумента для параметра T clazz, следовательно, T - это не Integer, а Class<Integer>.

Правильное объявление generi c будет:

public static void main(String[] args) {
    List<Integer> list = generic(Integer.class);
    System.out.println(list);
}

private static <T> List<T> generic(Class<T> clazz) {
    List<T> list = new ArrayList<>();
    return list;
}

Теперь, когда в программе нет «непроверенных» предупреждений, вы можете быть уверены, что во время выполнения не произойдет никакого загрязнения кучи. Если вам нужны дополнительные гарантии, вы можете использовать

public static void main(String[] args) {
    List<Integer> list = generic(Integer.class);
    System.out.println(list);
}

private static <T> List<T> generic(Class<T> clazz) {
    List<T> list = Collections.checkedList(new ArrayList<>(), clazz);
    list.add((T) Arrays.asList("create", "generic")); // will fail at runtime
    list.add((T) Arrays.asList("create", "generic2"));
    return list;
}

Однако, когда вы слушаете и отвечаете на предупреждения компилятора, вам не нужны дополнительные проверки во время выполнения.

0 голосов
/ 29 апреля 2020

Обобщения предоставляют проверки типов во время компиляции и стираются компилятором во время стирания типов . Таким образом, после стирания типа ваш метод будет выглядеть примерно так:

private static List generic(Object clazz) {
    List list = new ArrayList();
    list.add((Object) Arrays.asList("create", "generic"));
    list.add((Object) Arrays.asList("create", "generic2"));
    return list;
}

И ваш main метод будет выглядеть следующим образом:

List list = (List) generic(Integer.class);
System.out.println(list);

Так что нет причины для ClassCastException быть брошенным, потому что вы не пытаетесь выполнить какие-либо Integer указанные c действия с элементами вашего списка. Но вы получите его, как только сделаете, например:

list.forEach(integer -> System.out.println(integer.intValue()));

, потому что здесь он попытается привести ArrayList к Integer для вызова intValue().

Вывод: использование дженериков вместе с неконтролируемым кастингом совершенно бесполезно.

0 голосов
/ 29 апреля 2020

Поскольку вы объявили T как <T>, проверка типа во время выполнения (T) против Object. Поэтому приведение из List в порядке.

List объявлено как List<E>, поэтому дополнительная внутренняя проверка типов во время выполнения не может быть выполнена из List.add. Аналогично для List.toString.

Вы, однако, будете получать предупреждения (если в вашем компиляторе включена опция lint). Не игнорируйте предупреждения.

Вы можете использовать clazz.cast вместо T, хотя T не может быть параметризованным типом.

...