Java 8. Обобщения. Используя сырой тип. Неожиданная конвертация типов - PullRequest
0 голосов
/ 25 апреля 2018

Допустим, у меня есть класс с объявлением raw type в виде List (list1). Это простой пример:

public class Wildcards {
    public boolean contains(List list1, List<?> list2){

      /*for(Object element: list1) {
            if (list2.contains(element)) {
                return true;
            }
        }*/

        list1.add("12sdf34"); //insert String 

        return false;
    }
}

В list1 Я вставляю String значение. (Если бы я использовал неограниченные подстановочные знаки для list1 , как для list2 , это было бы более безопасно и было бы ошибкой компиляции). Однако, это необработанный тип.

Теперь давайте используем этот метод следующим образом:

List<Integer> list1 = new ArrayList<Integer>();
List<Double> list2 = new ArrayList<Double>();

System.out.println("Contains? " + (new Wildcards()).contains(list1, list2));
System.out.println("List1 element: " + list1.get(0));

Я не получу никаких ошибок и получу следующий результат:

Содержит? false

Элемент List1: 12sdf34

Может кто-нибудь объяснить, как это может быть, когда я инициализировал list1 как список целых чисел?

Ответы [ 2 ]

0 голосов
/ 25 апреля 2018

Ответ состоит из двух частей.Во-первых, генерики стираются во время компиляции .Вторая часть - это фактическая реализация ArrayList.Давайте начнем с стирания типа.

Во время компиляции все универсальные типы заменяются их верхней границей.Например, общий параметр <T extends Comparable<T>> сворачивается в Comparable<T>.Если верхняя граница не указана, она заменяется на Object.Это делает дженерики эффективным инструментом для проверки типов во время компиляции, но мы теряем всю информацию о типах во время выполнения. Project Valhalla может исправить или не исправить это в будущем.Поскольку ваш метод работает с необработанными и неограниченными типами, компилятор принимает Object в качестве универсального типа и, таким образом, list1.add("12sdf34"); проходит проверку типов.

Итак, почему бы вам не получить какое-то исключение во время выполнения?Почему ArrayList не "распознает", что значение, которое вы даете, имеет неправильный тип?Потому что ArrayList использует Object[] в качестве резервного буфера .Следующий логический вопрос: почему ArrayList использует Object[] вместо T[]?Из-за стирания типа: мы не можем создать экземпляр T или T[] во время выполнения.

Для вашей программы это означает, что ни ошибка компиляции, ни исключение времени выполнения не будут выброшены, и, следовательно, ваш ArrayList<Integer> может содержать String.Однако у вас возникнут проблемы, написав

...
System.out.println("Contains? " + (new Wildcards()).contains(list1, list2));
System.out.println("List1 element: " + list1.get(0))
int i = list1.get(0);

С точки зрения лексера, код все еще действителен.Но во время выполнения назначение сгенерирует ClassCastException.Это одна из причин, по которой необработанных типов следует избегать .

0 голосов
/ 25 апреля 2018

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

Вся проверка универсального типа выполненаво время компиляции, и компилятор не может выдавать ошибку для необработанных типов, иначе код, написанный для Java 1.4 (предварительные) или более старый, не будет компилироваться.Следовательно, вместо этого возникает предупреждение «rawtypes».

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