Downcasting в Java

В Java разрешено использование масштабирования, но при этом downcasting дает ошибку компиляции.

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

В этом случае, почему Java допускает downcasting, если он не может быть выполнен во время выполнения?
Есть ли практическое применение этой концепции?

public class demo {
  public static void main(String a[]) {
      B b = (B) new A(); // compiles with the cast, 
                         // but runtime exception - java.lang.ClassCastException
  }
}

class A {
  public void draw() {
    System.out.println("1");
  }

  public void draw1() {
    System.out.println("2");
  }
}

class B extends A {
  public void draw() {
    System.out.println("3");
  }
  public void draw2() {
    System.out.println("4");
  }
}

Ответ 1

Даункинг разрешен, когда есть вероятность, что он преуспеет во время выполнения:

Object o = getSomeObject(),
String s = (String) o; // this is allowed because o could reference a String

В некоторых случаях это не удастся:

Object o = new Object();
String s = (String) o; // this will fail at runtime, because o does not reference a String

Если приведение (например, последнее) не выполняется во время выполнения, ClassCastException будет брошено.

В других случаях это будет работать:

Object o = "a String";
String s = (String) o; // this will work, since o references a String

Обратите внимание, что некоторые приведения будут запрещены во время компиляции, потому что они вообще никогда не будут успешными:

Integer i = getSomeInteger();
String s = (String) i; // the compiler will not allow this, since i can never reference a String.

Ответ 2

Используя ваш пример, вы можете сделать:

public void doit(A a) {
    if(a instanceof B) {
        // needs to cast to B to access draw2 which isn't present in A
        // note that this is probably not a good OO-design, but that would
        // be out-of-scope for this discussion :)
        ((B)a).draw2();
    }
    a.draw();
}

Ответ 3

Я считаю, что это относится ко всем статически типизированным языкам:

String s = "some string";
Object o = s; // ok
String x = o; // gives compile-time error, o is not neccessarily a string
String x = (String)o; // ok compile-time, but might give a runtime exception if o is not infact a String

Эффект эффективно говорит: предположим, что это ссылка на класс cast и использовать его как таковой. Теперь давайте скажем, что o - это действительно целое число, если предположить, что это строка, не имеет смысла и даст неожиданные результаты, поэтому должна быть проверка времени выполнения и исключение для уведомления среды выполнения, что-то неправильно.

В практическом использовании вы можете писать код, работающий над более общим классом, но отсылать его в подкласс, если вы знаете, какой подкласс он и должен рассматривать как таковой. Типичным примером является переопределение Object.equals(). Предположим, что для Car существует класс:

@Override
boolean equals(Object o) {
    if(!(o instanceof Car)) return false;
    Car other = (Car)o;
    // compare this to other and return
}

Ответ 4

Мы все можем видеть, что предоставленный вами код не будет работать во время выполнения. Это потому, что мы знаем, что выражение new A() никогда не может быть объектом типа B.

Но это не то, как компилятор видит это. К тому времени, когда компилятор проверяет, разрешено ли приведение, он просто видит это:

variable_of_type_B = (B)expression_of_type_A;

И, как показали другие, этот вид актеров совершенно легален. Выражение справа может очень хорошо оценить объект типа B. Компилятор видит, что A и B имеют отношение подтипа, поэтому при просмотре кода "выражение" код может работать.

Компилятор не рассматривает специальный случай, когда он точно знает, какой тип объекта expression_of_type_A действительно будет иметь. Он просто видит статический тип как A и считает, что динамический тип может быть A или любым потомком A, включая B.

Ответ 5

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

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

Например, представьте, что типы B, C и D все расширяют тип A, а затем метод public A getSomeA() возвращает экземпляр B, C или D в зависимости от произвольно сгенерированного числа. Компилятор не может знать, какой именно тип времени выполнения будет возвращен этим методом, поэтому, если позже вы передадите результаты в B, нет способа узнать, будет ли приведение выполнено (или не выполнено). Поэтому компилятор должен предположить, что отбрасывание будет успешным.

Ответ 6

@Оригинальный плакат - см. встроенные комментарии.

public class demo 
{
    public static void main(String a[]) 
    {
        B b = (B) new A(); // compiles with the cast, but runtime exception - java.lang.ClassCastException 
        //- A subclass variable cannot hold a reference to a superclass  variable. so, the above statement will not work.

        //For downcast, what you need is a superclass ref containing a subclass object.
        A superClassRef = new B();//just for the sake of illustration
        B subClassRef = (B)superClassRef; // Valid downcast. 
    }
}

class A 
{
    public void draw() 
    {
        System.out.println("1");
    }

    public void draw1() 
    {
        System.out.println("2");
    }
}

class B extends A 
{
    public void draw() 
    {
        System.out.println("3");
    }

    public void draw2() 
    {
        System.out.println("4");
    }
}

Ответ 7

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

int intValue = 10;
Object objValue = (Object) intvalue;

Итак, теперь эта переменная objValue всегда может быть отключена до int, потому что объект, который был отличен, является Integer,

int oldIntValue = (Integer) objValue;
// can be done 

но поскольку objValue - это объект, он не может быть добавлен к String, потому что int нельзя отнести к String.

Ответ 8

Downcasting очень полезен в следующем фрагменте кода, который я использую это все время. Таким образом, доказательство того, что понижение полезно.

private static String printAll(LinkedList c)
{
    Object arr[]=c.toArray();
    String list_string="";
    for(int i=0;i<c.size();i++)
    {
        String mn=(String)arr[i];
        list_string+=(mn);
    }
    return list_string;
}

Я храню строку в связанном списке. Когда я извлекаю элементы связанного списка, объекты возвращаются. Чтобы получить доступ к элементам в виде строк (или любых других объектов класса), мне помогает downcasting.

Java позволяет нам скомпилировать понижающий код, доверяя нам, что мы делаем неправильную вещь. Тем не менее, если люди ошибаются, они попадают во время выполнения.

Ответ 9

Рассмотрим приведенный ниже пример

public class ClastingDemo {

/**
 * @param args
 */
public static void main(String[] args) {
    AOne obj = new Bone();
    ((Bone) obj).method2();
}
}

class AOne {
public void method1() {
    System.out.println("this is superclass");
}
}


 class Bone extends AOne {

public void method2() {
    System.out.println("this is subclass");
}
}

здесь мы создаем объект подкласса Bone и присваиваем ему ссылку суперкласса AOne, и теперь ссылка на суперкласс не знает о методе метода2 в подклассе i.e Bone во время компиляции. Поэтому нам нужно опустить эту ссылку суперкласса на ссылку подкласса, чтобы результирующая ссылка могла знать о наличии методов в подклассе i.e Bone

Ответ 10

Унизительное преобразование объектов невозможно. Only "DownCasting1 _downCasting1 = (DownCasting1) ((DownCasting2) downCasting1);" возможно

class DownCasting0 {
    public int qwe() {
        System.out.println("DownCasting0");
        return -0;
    }
}

class DownCasting1 extends DownCasting0 {
    public int qwe1() {
        System.out.println("DownCasting1");
        return -1;
    }
}

class DownCasting2 extends DownCasting1 {
    public int qwe2() {
        System.out.println("DownCasting2");
        return -2;
    }
}

public class DownCasting {

    public static void main(String[] args) {

        try {
            DownCasting0 downCasting0 = new DownCasting0();
            DownCasting1 downCasting1 = new DownCasting1();
            DownCasting2 downCasting2 = new DownCasting2();

            DownCasting0 a1 = (DownCasting0) downCasting2;
            a1.qwe(); //good

            System.out.println(downCasting0 instanceof  DownCasting2);  //false
            System.out.println(downCasting1 instanceof  DownCasting2);  //false
            System.out.println(downCasting0 instanceof  DownCasting1);  //false

            DownCasting2 _downCasting1= (DownCasting2)downCasting1;     //good
            DownCasting1 __downCasting1 = (DownCasting1)_downCasting1;  //good
            DownCasting2 a3 = (DownCasting2) downCasting0; // java.lang.ClassCastException

            if(downCasting0 instanceof  DownCasting2){ //false
                DownCasting2 a2 = (DownCasting2) downCasting0;
                a2.qwe(); //error
            }

            byte b1 = 127;
            short b2 =32_767;
            int b3 = 2_147_483_647;
//          long _b4 = 9_223_372_036_854_775_807; //int large number max 2_147_483_647
            long b4 = 9_223_372_036_854_775_807L;
//          float _b5 = 3.4e+038; //double default
            float b5 = 3.4e+038F; //Sufficient for storing 6 to 7 decimal digits
            double b6 = 1.7e+038;
            double b7 = 1.7e+038D; //Sufficient for storing 15 decimal digits

            long c1 = b3;
            int c2 = (int)b4;

            //int       4 bytes     Stores whole numbers from -2_147_483_648 to 2_147_483_647
            //float     4 bytes     Stores fractional numbers from 3.4e−038 to 3.4e+038. Sufficient for storing 6 to 7 decimal digits
            float c3 = b3; //logic error
            double c4 = b4; //logic error


        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

}

Ответ 11

Чтобы выполнить downcasting в Java и избежать исключений во время выполнения, воспользуйтесь ссылкой на следующий код:

if (animal instanceof Dog) {
  Dog dogObject = (Dog) animal;
}

Здесь Animal - родительский класс, а Dog - дочерний.
  instanceof - это ключевое слово, которое используется для проверки, содержит ли ссылочная переменная заданный тип ссылки на объект или нет.