Подтвердить что ты не робот

Как работает кастинг Java Object за сценой?

Возможный дубликат:
Как работает оператор трансляции Java?
Реализация Java casting

Мне всегда интересно, как работает создание объектов в Java. Я понимаю, что для примитивного типа он будет больше похож на двоичный уровень представления, но как насчет Object? Это вроде как Polymorphism или dynamic binding в том, что все будет определено во время выполнения? Например:

class Parent{
     void A(){}
}
class Child extends Parent{
     @Override
     void A(){}
}

Parent p = new Parent();
Child c = (Child) p;

Как это работает за сценой? Создает ли он новый экземпляр Child? А также, что произойдет, если вы попытаетесь выполнить бросок:

Child b = (Child) new Object();

И последний, когда приведение примитива к классу-оболочке:

Double d = (Double) 3.3;

Я знаю, что вам не нужно его бросать, но что, если вы это сделаете? Есть ли что-то существенное, что происходит на бэкэнд?

4b9b3361

Ответ 1

Когда вы используете явное приведение, в системе не создаются новые объекты (за исключением последнего случая, когда вы приводите примитивный тип к объектной обертке, поскольку double - это не такой объект, как Double). Обратите внимание, что это явное приведение не является необходимым из-за функции автобокса Java.

В вашем (Child) new Object() сценарий, вы получите ClassCastException, потому что Object не является для Child (хотя верно и обратное).

Ответ на ваш первый сценарий самый сложный. По сути, родительский класс рассматривается как интерфейс. При преобразовании Child Parent в Parent доступен только Parent API. Однако переопределенный метод все равно будет вызван. Итак, если вы делаете:

Parent p = (Parent) new Child();
p.a();

... Child public void a() будет вызываться, несмотря на то, что в настоящее время рассматривается через призму Parent класса. Однако если бы у вас был второй метод в Child, которого у Parent нет (скажем, например, public void b()), вы бы не смогли вызвать его, не приведя объект обратно к Child.

"За кулисами", как вы говорите, единственная новая вещь, которая создается, - это еще одна ссылка на объект, которая указывает на тот же объект. Вы можете иметь столько ссылок, сколько хотите на один и тот же объект. Рассмотрим этот пример:

Parent p = new Parent();
Parent p1 = p;
Parent p2 = p;
Parent p3 = p2;

Здесь есть четыре ссылки (p, p1, p2 и p3), каждая из которых указывает на тот же объект, который вы создали с помощью new Parent().

Я бы, вероятно, поспорил с философской точки зрения, что это создание новых ссылок на самом деле явное, а не закулисное, когда вы говорите, что Parent p = something.

Ссылки:

Ответ 2

Простой ответ на ваш главный вопрос - нет. Все приведение происходит во время проверки синтаксиса.

Приведение влияет на то, как средство проверки синтаксиса смотрит на объект, оно не влияет на сам объект, дочерний элемент, являющийся родительским, все еще остается дочерним.

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

Ответ 3

Согласно этому: checkcast, он проверяет, является ли ссылка назначаемой. Если это так, стек не изменяется, и операции с этой ссылкой сохраняются.

Так что если у вас есть:

 Child c = ( Child )  anyObject; 
 c.sayHi();

Если приведение выполнено успешно, может быть вызван метод sayHi:

Если objectref может быть приведен к разрешенному классу, массиву или типу интерфейса, стек операндов не изменяется; в противном случае инструкция checkcast генерирует исключение ClassCastException.

Здесь "байт-код"

$ cat CastDemo.java 
class Parent {}
class Child extends Parent {}
class Main {
    Child c = (Child) new Parent();
}
$ javap -c Main
Compiled from "CastDemo.java"
class Main {
  Child c;

  Main();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0       
       5: new           #2                  // class Parent
       8: dup           
       9: invokespecial #3                  // Method Parent."<init>":()V
      12: checkcast     #4                  // class Child
      15: putfield      #5                  // Field c:LChild;
      18: return        
}

Ответ 4

Прежде всего, будьте очень осторожны, чтобы не путать конверсию с кастингом. Они могут совместно использовать поверхностный синтаксис, но это очень разные процессы.

В Java вы можете сбрасывать объект на любой тип, но во время выполнения вы получите ClassCastException, если объект на самом деле не совместим с целевым типом. Это происходит на уровне байт-кода: есть команда байт-кода, предназначенная для downcasting.

Child c = (Child) new Object();

безоговорочно приведет к ClassCastException.

Double d = 3.3; // note: no explicit casting needed

выполнит autoboxing в экземпляр Double. Итак, здесь создается новый экземпляр.

Нормальная, успешная dowcast может выглядеть так:

Object o = "a";
String s = (String)o;

Здесь объекты не создаются: только значение o копируется в s. Значение является ссылкой.

Ответ 5

Вниз вниз объект не делает ничего с этим объектом. За кулисами компилятор будет вводить checkcast операцию байт-кода. Если p на самом деле не является экземпляром Child, будет выбрано исключение. В противном случае вы в основном имеете (тип-) безопасную ссылку на тот же объект с другим, более конкретным типом.


Child b = (Child) new Object();

Это не работает с ClassCastException. JVM сравнивает getClass() с new Object() с Child.class. Поскольку Object.class не является подклассом Child.class, генерируется исключение.


Double d = (Double) 3.3;

Здесь не требуется даже кастинг, это тоже работает: Double d = 3.3. За кулисами это переводится на:

Double d = Double.valueOf(3.3);

Это называется Autoboxing.