Я пытаюсь отладить проблему с использованием ClassCastException в Java. В интересах решения проблемы мне нужно знать, что происходит, когда я отбрасываю объект из определенного типа. Может ли кто-нибудь объяснить мне, как работает оператор трансляции Java на уровне Java и уровне JVM?
Как работает оператор трансляции Java?
Ответ 1
Достаточно ли JLS?
Преобразование кастинга применяется к операнду оператора трансляции (§15.16): тип выражения операнда должен быть преобразован в тип, явно названный оператором литья. Контексты каста позволяют использовать:
- преобразование идентичности (§5.1.1)
- расширение примитивного преобразования (§5.1.2)
- сужение примитивного преобразования (§5.1.3)
- расширение ссылочного преобразования (п. 5.1.5), необязательно сопровождаемое непроверенным преобразованием (§5.1.9)
- сужение ссылочного преобразования (§5.1.6), необязательно сопровождаемое непроверенным преобразованием
- преобразование бокса (§5.1.7)
- преобразование распаковки (§5.1.8).
Собственно, возможно эта часть более актуальна:
Подробные правила законности компиляции времени преобразования кастинга значения ссылочного типа времени компиляции S для ссылочного типа времени компиляции T следующие::
- Если S - тип класса:
- Если Tтип класса, то либо | S | <: | T |, или | T | & Л;: | <Я > S |; в противном случае время компиляции возникает ошибка. Кроме того, если существует супертип X of T, а супертип Y S, что обе X и Y доказуемо различны параметризованные типы (п. 4.5), и что стирания X и Y совпадают, время компиляции возникает ошибка.
- Если T - тип интерфейса:
- Если S не является
final
класса (§8.1.1), то, если существует супертип X of T, а супертип Y of S, так что оба X и Y доказуемо различные параметризованные типы, и что стирания X и Yсовпадают, ошибка времени компиляции имеет место. В противном случае, литье всегда во время компиляции (потому что даже если S не реализует T, подкласс S может).- Если S является
final
(§8.1.1), то S должен реализовать T, или возникает ошибка времени компиляции.
- Если Tявляется переменной типа, то это алгоритм применяется рекурсивно, используя верхнюю оценку T в место T.
- Если Tтип массива, то S должен быть класс
Object
, или ошибка компиляции.- Если Sтип интерфейса:
- Если Tтип массива, то T должен реализовать S или время компиляции возникает ошибка.
- Если T - тип, который не является
final
(§8.1.1), то, если существует супертип X of T, а супертип Y of S, так что оба X и Y доказуемо различные параметризованные типы, и что стирания X и Yсовпадают, ошибка времени компиляции имеет место. В противном случае, литье всегда во время компиляции (потому что даже если T не реализует S, подкласс T может).- Если Tтип
final
, затем:
- Если S не является параметризованным тип или необработанный тип, тогда T должен реализовать S, а литой статически известны как правильные, или ошибка компиляции.
- В противном случае, S является либо параметризованным тип, который является вызовом некоторых объявление типового типа G или необработанный тип, соответствующий общему type G. То есть должен существовать супертип X of T, что X является вызов G, или ошибка компиляции. Более того, если S и Xявляются явно различимыми параметризованными типы, то ошибка времени компиляции имеет место.
- Если Sпеременная типа, то этот алгоритм применяется рекурсивно, используя верхняя грань S вместо <Я > S.
- Если S - тип массива SC [], т.е. массив компонентов type SC:
- Если T является class type, то если T не является
Object
, то a ошибка времени компиляции (потому чтоObject
- единственный класс тип, которому могут быть назначены массивы).- Если Tявляется типом интерфейса, тогда ошибка компиляции <Я > Tэто тип
java.io.Serializable
или типCloneable
, только интерфейсы, реализованные с помощью массивов.- Если Tявляется переменной типа, тогда:
- Если верхняя граница T является
Object
или типjava.io.Serializable
или типCloneable
, или тип S может юридически поддаваться рекурсивно применяя эти правила, тогда актерский состав юридический (хотя и неконтролируемый).- Если верхний граница T - это тип массива TC [], то ошибка времени компиляции происходит, если тип SC [] не может быть отброшенным на TC [] рекурсивным применение этих времени компиляции правила литья.
- В противном случае, ошибка компиляции.
- Если Tтип массива TC [], т.е. массив компонентов типа TC, то возникает ошибка времени компиляции если не выполнено одно из следующих утверждений:
- TC и SC - это такой же примитивный тип.
- TC и SC являются ссылочными типами и типами SC может быть отличен до TCрекурсивное применение этих правила компиляции для кастинга.
Совершенно ясно, не так ли?: D
Другими словами, это лучшее, что я могу сделать, не зная подробностей о вашей проблеме.
Ответ 2
Вероятная причина метасимметрии класса - это не только соответствие типов, но и их загрузка одним и тем же загрузчиком классов.
Вы должны иметь возможность сбросить не только иерархию типов, но и идентификатор загрузчика классов для каждого класса.
Подобные проблемы нередко встречаются в средах приложений в стиле сервера приложений, где код приложения и код подструктуры умышленно изолированы - например, если системные классы случайно включены в JAR-приложения, вы можете иметь две копии "того же" класса в JVM и жизнь запутывается
Ответ 3
Другие полезные и авторитетные ссылки содержатся в Спецификации виртуальной машины Java, в частности §2.6.5, "Сужение ссылочных преобразований" , и особенно определение инструкции checkcast
.
Ответ 4
Кастинг утверждает, что тип среды выполнения совместим с данным статическим типом и, таким образом, позволяет вам вызывать методы этого типа на объекте.
Здесь obj - объект Integer, но доступен только с ссылкой на Object:
Object obj = new Integer(1);
Кастинг позволяет снова рассматривать его как целое (или некоторый суперкласс из Integer):
System.out.println(((Integer) obj).intValue());
ClassCastException происходит, когда заданный статический тип не соответствует типу времени выполнения объекта:
System.out.println(((Float) obj).intValue()); // runtime error
Вы можете найти тип выполнения любого объекта с помощью getClass() и различных методов класса:
System.out.println(obj.getClass()); // prints "class java.lang.Integer"