Как работает оператор приведения Java? - PullRequest
11 голосов
/ 08 мая 2009

Я пытаюсь отладить проблему, связанную с ClassCastException в Java. В интересах решения проблемы мне нужно знать, что происходит, когда я преобразую объект в конкретный тип. Может ли кто-нибудь объяснить мне, как работает оператор приведения Java на уровне Java и JVM?

Ответы [ 4 ]

10 голосов
/ 08 мая 2009

Достаточно ли хорош 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 | <: | <i>T | или | T | <: | <i>S |; в противном случае время компиляции ошибка происходит. Кроме того, если есть существует супертип X из T и супертип Y из S , так что оба X и Y доказуемо различимы параметризованные типы (§4.5), и что стирание X и Y - то же самое, время компиляции ошибка происходит.
    • Если T - это тип интерфейса:
      • Если S не является final класс (§8.1.1), тогда, если существует супертип X из T и супертип Y из S , так что оба X и Y доказуемо отдельные параметризованные типы, и это стирание X и Y одинаковы, ошибка времени компиляции происходит. В противном случае, актерский состав всегда законно во время компиляции (потому что даже если S не реализует T , подкласс S может).
      • Если S является final класс (§8.1.1), тогда S должен реализовать T , или происходит ошибка времени компиляции.

    • Если Т переменная типа, то это алгоритм применяется рекурсивно, используя верхнюю границу T в место Т .
    • Если T тип массива, тогда S должен быть класс Object или происходит ошибка времени компиляции.
  • Если S тип интерфейса:
    • Если T тип массива, тогда T должен реализовать S или время компиляции ошибка происходит.
    • Если T - это тип, который не является final (§8.1.1), тогда, если существует супертип X из T и супертип Y из S , так что оба X и Y доказуемо отдельные параметризованные типы, и это стирание X и Y одинаковы, ошибка времени компиляции происходит. В противном случае, актерский состав всегда законно во время компиляции (потому что даже если T не реализует S , подкласс T может).
    • Если T тип final, затем:
      • Если S не параметризован тип или необработанный тип, то T должен реализовать S , и приведение статически известно, что это правильно, или происходит ошибка времени компиляции.
      • В противном случае, S является параметризованным тип, который является вызовом некоторых объявление универсального типа G или необработанный тип, соответствующий универсальному объявление типа G . То есть должен существовать супертип X из T, такой, что X является вызов G , или происходит ошибка времени компиляции. Кроме того, если S и X доказуемо четко параметризованы типы затем ошибка времени компиляции происходит.
  • Если S переменная типа, то этот алгоритм применяется рекурсивно, используя верхняя граница S вместо S .
  • Если S - это тип массива SC [], то есть массив компонентов тип SC :
    • Если T является тип класса, тогда если T не Object, затем происходит ошибка времени компиляции (потому что Object единственный класс тип, которому могут быть назначены массивы).
    • Если Т тип интерфейса, то ошибка времени компиляции T это тип java.io.Serializable или тип Cloneable, только интерфейсы, реализованные массивами.
    • Если Т является переменной типа, тогда:
      • Если верхний предел T является Object или тип java.io.Serializable или тип Cloneable или переменная типа, которую может S на законных основаниях быть рекурсивным применяя эти правила, то актерский состав законно (хотя и не проверено).
      • Если верхний граница T является типом массива TC [] , затем ошибка времени компиляции происходит, если тип SC [] не может быть приведенным к TC [] рекурсивным Применение этих времени компиляции правила кастинга.
      • В противном случае, происходит ошибка времени компиляции.
    • Если T тип массива TC [], то есть массив компонентов типа TC , тогда происходит ошибка времени компиляции если не выполняется одно из следующих условий:
    • TC и SC являются тот же примитивный тип.
    • TC и SC являются ссылочными типами и типами SC можно привести к TC с помощью рекурсивное применение этих правила времени компиляции для приведения.

Совершенно ясно, не так ли? : D

Другими словами, это лучшее, что я могу сделать, не зная больше деталей о вашей проблеме.

6 голосов
/ 08 мая 2009

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

У вас должна быть возможность вывести не только иерархию типов, но и идентификатор загрузчика классов для каждого класса.

Подобные проблемы не редкость в средах в стиле appserver, где код приложения и код инфраструктуры нарочито обособлены - например, если системные классы случайно включены в JAR-файлы applciation, у вас может быть две копии «одного и того же» класса в JVM и жизнь запутывается

3 голосов
/ 08 мая 2009

Приведение утверждает, что тип среды выполнения объекта совместим с данным статическим типом, и, таким образом, позволяет вызывать методы этого типа для объекта.

Здесь obj - это объект типа Integer, но доступный только через ссылку на объект:

Object obj = new Integer(1);

Приведение позволяет снова рассматривать его как целое число (или некоторый суперкласс Integer):

System.out.println(((Integer) obj).intValue());

ClassCastException происходит, когда указанный статический тип не соответствует типу времени выполнения объекта:

System.out.println(((Float) obj).intValue()); // runtime error

Вы можете найти тип среды выполнения любого объекта, используя getClass () и различные методы Class:

System.out.println(obj.getClass()); // prints "class java.lang.Integer"
3 голосов
/ 08 мая 2009

Другие полезные и авторитетные ссылки можно найти в Спецификации виртуальной машины Java, в частности §2.6.5, «Сужающие преобразования ссылок», и особенно определение checkcast инструкция.

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