Совместимость Generics с унаследованным кодом - почему foreach работает во время выполнения, пока итератор работает нормально - PullRequest
0 голосов
/ 26 апреля 2020

У меня есть следующий тестовый код. Я пытаюсь понять взаимодействие между Generics и legacy.

List myList = new ArrayList();
myList.add("abc");
myList.add(1);
myList.add(new Object());

System.out.println("Printing the unchecked list");
Iterator iterator = myList.iterator();
while (iterator.hasNext()) {
  System.out.println(iterator.next());
}

List<String> strings = myList;

System.out.println("Printing the unchecked list assigned to List<String> using iterator");
Iterator stringIterator = strings.iterator();
while (stringIterator.hasNext()) {
  System.out.println(stringIterator.next());  // this works fine! why? shouldn't it fail converting the 1 (int/Integer) to String?
}

System.out.println("Printing the unchecked list assigned to List<String> using for");
for (int i = 0; i != strings.size(); i++) {
  System.out.println(strings.get(i));  // blows up as expected in the second element, why?
}

System.out.println("Printing the unchecked list assigned to List<String> using foreach");
for (String s : strings) {
  System.out.println(s);  // blows up as expected in the second element, why?
}

Почему iterator.next работает нормально, когда я пытаюсь его напечатать, в то время как System.out.println взрывается, как и ожидалось, когда я выполняю итерацию с использованием for петли?

1 Ответ

4 голосов
/ 26 апреля 2020

Ключевым моментом, который следует помнить о дженериках, является то, что это просто способ пропустить приведение из исходного кода. Приведения вставляются компилятором. Итак, код не работает (или не дает сбой) из-за наличия (или отсутствия) приведения.

  1. это работает отлично! почему?

Поскольку stringIterator является необработанным, stringIterator.next() не приводится ни к чему: он просто читается как Object, который является стертым типом возврата.

взрывается, как и ожидалось, во втором элементе, почему?

strings это List<String>, поэтому результат strings.get(i) предполагается равным String, и println(String) выбирается для вызова, а не println(Object). Таким образом, приведение вставлено. strings.get(1) не является String, так что это не так с ClassCastException.

Интересно, если бы вы пробовали это с List<Integer>, это бы не сработало, потому что println(Object) будет вызван, и никакое приведение не будет необходимо.

взрывается, как и ожидалось, во втором элементе, почему?

Поскольку вставлено приведение к String, чтобы присвоить элемент String s.

Обратитесь к JLS 14.14.2 , чтобы узнать, как выглядит расширенная форма для l oop:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
    {VariableModifier} TargetType Identifier =
        (TargetType) #i.next();
    Statement
}

Итак, ваш код эквивалентен:

for (Iterator<String> it = strings.iterator(); it.hasNext(); ) {
  String s = (String) it.next();  // Actually, your code fails on this line.
  System.out.println(s);
}
...