Почему этот метод позволяет хранить двойной тип в ArrayList типа Integer? - PullRequest
1 голос
/ 11 мая 2019

У меня есть метод, который должен возвращать экземпляр заданного класса коллекции с заданными записями. Реализация показана ниже.

public static <E, C extends Collection<E>> C
         initalizeAndAddToCollection(Class<C> clazz, E... entries) {
    //Collection object
    Collection<E> collection;
    //try to invoke default constructor of C
    try {
        collection = clazz.getDeclaredConstructor().newInstance();
    } catch (InstantiationException | IllegalAccessException |
             InvocationTargetException | NoSuchMethodException e) {
        throw new RuntimeException();
    }
    //Add elements to collection
    for (E entry: entries)
        collection.add(entry);
    return (C) collection;
}

Проблема в том, что следующий код выполняется, хотя double не может быть сохранен в списке типа Integer

 //Create and instantiate an ArrayList with element 1.0
 ArrayList<Integer> list = initalizeAndAddToCollection(ArrayList.class, 1.0);
 System.out.print(list.get(0));

Почему этот код выполняется и как мне сделать так, чтобы он приводил к ошибке компиляции или времени выполнения?

Редактировать: я заметил, list.get(0).getClass() генерирует исключение, но я не уверен, почему либо (или почему предыдущий код не делает).

Ответы [ 2 ]

2 голосов
/ 11 мая 2019

В Java дженерики являются функцией только во время компиляции, потому что они реализованы с помощью типа стирания . Поэтому, хотя вы можете думать, что вы создаете ArrayList<Integer> во время выполнения с помощью вызова newInstance, вы на самом деле просто создаете ArrayList.

Короче говоря, отражение нарушает безопасность типов для обобщений в Java.

1 голос
/ 11 мая 2019

Этот код выдает предупреждение при компиляции. В предупреждении говорится:

Note: MyTest.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

А на самом деле, когда вы делаете то, что выдает 4 предупреждающих сообщения:

MyTest.java:5: warning: [unchecked] Possible heap pollution from parameterized vararg type E
        initalizeAndAddToCollection(Class<C> clazz, E... entries) {
                                             ^
  where E,C are type-variables:
    E extends Object declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
    C extends Collection<E> declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)


MyTest.java:18: warning: [unchecked] unchecked cast
        return (C) collection;
                   ^
  required: C
  found:    Collection<E>
  where E,C are type-variables:
    E extends Object declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
    C extends Collection<E> declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
MyTest.java:23: warning: [unchecked] unchecked method invocation: method initalizeAndAddToCollection in class MyTest is applied to given types
             initalizeAndAddToCollection(ArrayList.class, 1.0);
                                        ^
  required: Class<C>,E[]
  found: Class<ArrayList>,double
  where C,E are type-variables:
    C extends Collection<E> declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
    E extends Object declared in method <E,C>initalizeAndAddToCollection(Class<C>,E...)
MyTest.java:23: warning: [unchecked] unchecked conversion
             initalizeAndAddToCollection(ArrayList.class, 1.0);
                                        ^
  required: ArrayList<Integer>
  found:    ArrayList
4 warnings

Эти предупреждения объясняют, почему этот, казалось бы, некорректный код компилируется. Вы делаете то, что компилятор скажет , что вы ошиблись.


Почему этот код выполняется.

Потому что это не нарушает безопасность типов во время выполнения.

  • Тип стирания означает, что в коллекции фактически хранятся ссылки на Object.

  • Когда вы затем делаете System.out.print(list.get(0));, тип параметра для вызова print равен Object. Это означает, что не требуется явное приведение к Integer.

и как мне сделать так, чтобы это приводило к ошибке компиляции или времени выполнения?

Если вам нужна ошибка во время компиляции, скажите компилятору обрабатывать предупреждения как ошибки. (Или проверьте предупреждения в выводе компиляции.)

Если вам нужна ошибка времени выполнения, вам нужно добавить некоторые явные проверки типов времени выполнения, вероятно, в вашем методе initalizeAndAddToCollection.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...