ClassCastexception в Java Foreach Loop
-
28-09-2019 - |
Вопрос
В каких обстоятельствах CASSCACTEXCECTION произойдет в коде ниже:
import java.util.Arrays;
import java.util.List;
public class Generics {
static List getObjects() {
return Arrays.asList(1, 2, 3);
}
public static void main(String[] args) {
List<String> list = getObjects();
for (Object o : list) { // ClassCastException?
System.out.println(o);
}
}
}
У нас был аналогичный случай в производственной среде (плохой практике, я знаю), и клиент предоставил журнал с классовом CASSEXCECTION на линии с комментарием, но я не могу его воспроизвести. есть идеи?
Я знаю, что JVM создает итератор на заднем плане при использовании Foreach, но он может создать необработанный итератор в некоторых случаях и параметризованный в других случаях?
Обновлять:Я также посмотрел на генерируемый байткод и в Windows, используя JDK 1.6.0_21-B07 NO контрольная передача сделан. Интересный :)
Вот главный метод:
Общественная статическая пустота Main (Java.lang.string []); Код: 0: invoxetatic # 34; // Метод getObjects :() ljava / util / list; 3: ASTORE_1 4: ALOAD_1 5: InvokeInterface # 36, 1; // Interfacemethod Java / UTIL / LIST.ТРИЧЕНИЕ :() LJAVA / UTIL / ITERATOR; 10: ASTORE_3 11: GOTO 28 14: ALOAD_3 15: InvokeInterface # 42, 1; // Interfacemethod Java / Util / iTerator.next :() ljava / lang / объект; 20: ASTORE_2 21: GetStatic # 48; // поле Java / lang / system.out: ljava / io / printstream; 24: Aload_2 25: InvokeWirtual # 54; // метод java / io / printstream.println: (ljava / lang / ebject;) v 28: Aload_3 29: InvokeInterface # 60, 1; // Interfacemethod Java / Util / iTerator.hasnext :() Z 34: IFNE 14 37: return
Спасибо всем за ответы!
Обновление 2.: Я вводил в заблуждение с ECLIPSE IDE, который использует его собственный компилятор Так что на самом деле байт-код выше это, сгенерированное с использованием Compiler Eclipse. Отказ Смотреть здесь Как вручную скомпилировать код с Eclipse. В заключении Eclipse Compiler генерирует другой байт-код из компилятора Солнца в некоторых случаях, независимо от платформы, чехол, описанный здесь, являющийся одним.
Решение
Разве этот код не должен всегда бросать ClassCastException
? Это делает для меня, используя Sun Java 6 компилятора и время выполнения (на Linux). Вы отличаете Integer
С String
с. Созданный итератор будет Iterator<String>
, но тогда он пытается получить доступ к первому элементу, который является Integer
, И так не удается.
Это становится более понятнее, если вы измените свой массив так:
return Arrays.asList("one", 2, 3);
Теперь петля на самом деле работает для первого элемента, потому что первый элемент является String
и мы видим вывод; тогда Iterator<String>
Не удается во второй, потому что это не строка.
Ваш код работает, если вы просто используете универсальный List
вместо конкретного:
List list = getObjects();
for (Object o : list) {
System.out.println(o);
}
... или, конечно, если вы используете List<Integer>
, так как содержимое Integer
с. Что вы делаете сейчас, вызывают предупреждение компилятора - Note: Generics.java uses unchecked or unsafe operations.
- и по хорошей причине.
Эта модификация также работает:
for (Object o : (List)list)
... предположительно потому, что в этот момент вы имеете дело с Iterator
, а не а Iterator<String>
.
Божо сказал, что он не видит эту ошибку в Windows XP (не упомянул, какой компилятор и время выполнения, но я предполагаю, что вы думаете, что вы не видите его (или не надежно), так что есть некоторые Чувствительность реализации здесь, но нижняя строка: не используйте List<String>
взаимодействовать с List
из Integer
с. :-)
Вот файл, который я составлен:
import java.util.Arrays;
import java.util.List;
public class Generics {
static List getObjects() {
return Arrays.asList("one", 2, 3);
}
public static void main(String[] args) {
List<String> list = getObjects();
for (Object o : list) { // ClassCastException?
System.out.println(o);
}
}
}
Вот компиляция:
TJC @ FORGE: ~ / TEMP $ JAVAC Generics.java Примечание: Generics.java использует незаменимые или небезопасные операции. Примечание: перекомпилируйте с -xlint: не проверяют детали.
Вот беги:
TJC @ FORGE: ~ / TEMP $ Java Generics Одним из исключений в потоке «Главная» java.lang.classcastexception: java.lang.integer нельзя бросить на java.lang.string по сравнению с Enterics.
Линия 12 for
утверждение. Обратите внимание, что он выводил первый элемент, потому что я изменил это на String
. Отказ Это не выводило других. (И до того, как я сделал это изменение, это не удалось немедленно.)
Вот компилятор, который я использую:
TJC @ FORGE: ~ / TEMP $, javac / usr / bin / javac tjc @ forge: ~ / temp $ ll / usr / bin / javac lrwxrwxrwx 1 root 23 2010-09-30 16:37 / usr / bin / javac -> / etc / albolativaties / Javac * TJC @ FORGE: ~ / TEMP $ LL / etc / albileaties / javac lrwxrwxrwx 1 root 33 2010-09-30 16:37 / etc / albileaties / javac -> / usr / lib / lib / lib / javac JVM / Java-6-Sun / Bin / Javac *
Вот разборка, которая показывает checkcast
:
TJC @ FORGE: ~ / TEMP $ JAVAP -C Generics, составленные из «Generics.java» общественных общего класса, расширяют Java.lang.Object {общедоступные универсальные (); Код: 0: ALOD_0 1: invoxial # 1; // Метод java / lang / объект "." :() v 4: возврат статической java.util.list getObjects (); Код: 0: iconst_3 1: Anewarray # 2; // Класс Java / IO / Serializable 4: DUP 5: iconst_0 6: LDC # 3; // String One 8: Aastore 9: DUP 10: iconst_1 11: iconst_2 12: invoxetatic # 4; // метод java / lang / integer.valueof: (i) ljava / lang / integer; 15: ASASTORE 16: DUP 17: iconst_2 18: iconst_3 19: invoxetatic # 4; // метод java / lang / integer.valueof: (i) ljava / lang / integer; 22: AASTORE 23: Invoxetatic # 5; // Метод java / util / actrays.aslist: ([ljava / lang / ebject;) ljava / util / list; 26: Areturn Public Static Void Main (Java.lang.string []); Код: 0: invoxetatic # 6; // Метод getObjects :() ljava / util / list; 3: ASTORE_1 4: ALOAD_1 5: InvokeInterface # 7, 1; // Interfacemethod Java / UTIL / LIST.ТРИЧЕНИЕ :() LJAVA / UTIL / ITERATOR; 10: ASTORE_2 11: ALOOD_2 12: InvokeInterface № 8, 1; // Interfacemethod Java / UTIL / ITERATOR.HASNEXT :() Z 17: IFEQ 40 20: ALOAD_2 21: InvokeInterface # 9, 1; // Interfacemethod Java / Util / iTerator.next :() ljava / lang / объект; 26: Checkcast # 10; // Класс Java / Lang / String 29: ASTORE_3 30: GetStatic # 11; // поле Java / lang / system.out: ljava / io / printstream; 33: Aload_3 34: Invokewirtual # 12; // метод java / io / printstream.println: (ljava / lang / объект;) v 37: goto 11 40: return}
Опять же, внизу должна быть: не используйте List<String>
взаимодействовать с List
который содержит вещи, которые не являются String
с. :-)
Другие советы
Я тоже не могу его воспроизвести, но я обнаружил следующие ошибки, которые должны быть исправлены:
- делать
getObjects()
возвратList<Integer>
, а не сырой тип - Не ожидайте
List<String>
, но аList<Integer>
вместо - При итерации, петли
for (Integer o : list)
Проблема это метод static List getObjects() {
Возвращает общий (не параметризованный) List
. Отказ И вы назначаете это List<String>
. Отказ Эта линия должна дать компилятору предупреждение, и это должно было сообщить проблему.
Эта часть :
List<String> list = getObjects();
for (Object o : list) { // ClassCastException?
System.out.println(o);
}
Будет упрощен как
List<String> list = getObjects();
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
Object o = iterator.next();
System.out.println(o);
}
Но реализация итератора попытается отбрасывать при звонке next()
Метод Содержание Отправить по Iterator
в String
.
Вот почему у вас есть CCE.
Решения:
Либо используйте дженерики везде или не используйте их, но это действительно важно, чтобы быть последовательным. Если вы вернули List<Integer>
или а List<? super Integer>
Вы бы видели эту проблему в момент компиляции.