简体   繁体   English

向下转换 Java

[英]Downcasting in Java

Upcasting is allowed in Java, however downcasting gives a compile error. Java 中允许向上转型,但是向下转型会产生编译错误。

The compile error can be removed by adding a cast but would anyway break at the runtime.可以通过添加强制转换来消除编译错误,但无论如何都会在运行时中断。

In this case why Java allows downcasting if it cannot be executed at the runtime?在这种情况下,如果 Java 不能在运行时执行,为什么允许向下转换?
Is there any practical use for this concept?这个概念有什么实际用途吗?

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");
  }
}

Downcasting is allowed when there is a possibility that it succeeds at run time:当有可能在运行时成功时,向下转换是允许的:

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

In some cases this will not succeed:在某些情况下,这不会成功:

Object o = new Object();
String s = (String) o; // this will fail at runtime, because o doesn't reference a String

When a cast (such as this last one) fails at runtime a ClassCastException will be thrown.当强制转换(例如最后一个)在运行时失败时,将抛出ClassCastException

In other cases it will work:在其他情况下,它会起作用:

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

Note that some casts will be disallowed at compile time, because they will never succeed at all:请注意,在编译时将不允许某些强制转换,因为它们根本不会成功:

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

Using your example, you could do:使用您的示例,您可以执行以下操作:

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();
}

I believe this applies to all statically typed languages:我相信这适用于所有静态类型语言:

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

The typecast effectively says: assume this is a reference to the cast class and use it as such.类型转换有效地表示:假设这是对转换类的引用并以此方式使用它。 Now, lets say o is really an Integer, assuming this is a String makes no sense and will give unexpected results, thus there needs to be a runtime check and an exception to notify the runtime environment that something is wrong.现在,假设 o确实是一个整数,假设这是一个字符串是没有意义的,并且会给出意想不到的结果,因此需要有一个运行时检查和一个异常来通知运行时环境有问题。

In practical use, you can write code working on a more general class, but cast it to a subclass if you know what subclass it is and need to treat it as such.在实际使用中,您可以编写处理更通用类的代码,但如果您知道它是什么子类并且需要将其视为子类,则可以将其转换为子类。 A typical example is overriding Object.equals().一个典型的例子是覆盖 Object.equals()。 Assume we have a class for Car:假设我们有一个 Car 类:

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

We can all see that the code you provided won't work at run time.我们都可以看到您提供的代码在运行时不起作用。 That's because we know that the expression new A() can never be an object of type B .那是因为我们知道表达式new A()永远不可能B类型的对象。

But that's not how the compiler sees it.但这不是编译器的看法。 By the time the compiler is checking whether the cast is allowed, it just sees this:当编译器检查是否允许强制转换时,它只会看到:

variable_of_type_B = (B)expression_of_type_A;

And as others have demonstrated, that sort of cast is perfectly legal.正如其他人所证明的那样,这种类型的演员是完全合法的。 The expression on the right could very well evaluate to an object of type B .右边的表达式可以很好地评估为类型B的对象。 The compiler sees that A and B have a subtype relation, so with the "expression" view of the code, the cast might work.编译器看到AB具有子类型关系,因此使用代码的“表达式”视图,转换可能会起作用。

The compiler does not consider the special case when it knows exactly what object type expression_of_type_A will really have.当编译器确切地知道expression_of_type_A将真正具有什么对象类型时,它不会考虑特殊情况。 It just sees the static type as A and considers the dynamic type could be A or any descendant of A , including B .它只是看到静态类型为A ,并认为动态类型可以是A或任何后代A ,包括B

In this case why Java allows downcasting if it cannot be executed at the runtime?在这种情况下,如果 Java 不能在运行时执行,为什么它允许向下转换?

I believe this is because there is no way for the compiler to know at compile-time if the cast will succeed or not.我相信这是因为编译器无法在编译时知道转换是否成功。 For your example, it's simple to see that the cast will fail, but there are other times where it is not so clear.对于您的示例,很容易看出强制转换将失败,但在其他情况下则不太清楚。

For instance, imagine that types B, C, and D all extend type A, and then a method public A getSomeA() returns an instance of either B, C or D depending on a randomly generated number.例如,假设类型 B、C 和 D 都扩展了类型 A,然后方法public A getSomeA()根据随机生成的数字返回 B、C 或 D 的实例。 The compiler cannot know which exact run-time type will be returned by this method, so if you later cast the results to B , there is no way to know if the cast will succeed (or fail).编译器无法知道此方法将返回哪种确切的运行时类型,因此如果稍后将结果转换为B ,则无法知道转换是否成功(或失败)。 Therefore the compiler has to assume casts will succeed.因此编译器必须假设强制转换会成功。

Downcast works in the case when we are dealing with an upcasted object.当我们处理向上转换的对象时,向下转换有效。 Upcasting:上行:

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

So now this objValue variable can always be downcasted to int because the object which was cast is an Integer ,所以现在这个objValue变量总是可以向下转换为int因为被转换的对象是一个Integer

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

but because objValue is an Object it cannot be cast to String because int cannot be cast to String .但是因为objValue是一个 Object 它不能转换为String因为int不能转换为String

@ Original Poster - see inline comments. @ 原始海报 - 见内嵌评论。

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");
    }
}

Downcasting is very useful in the following code snippet I use this all the time.在我一直使用的以下代码片段中,向下转换非常有用。 Thus proving that downcasting is useful.因此证明向下转换是有用的。

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;
}

I store String in the Linked List.我将字符串存储在链接列表中。 When I retrieve the elements of Linked List, Objects are returned.当我检索链接列表的元素时,返回对象。 To access the elements as Strings(or any other Class Objects), downcasting helps me.要将元素作为字符串(或任何其他类对象)访问,向下转换对我有帮助。

Java allows us to compile downcast code trusting us that we are doing the wrong thing. Java 允许我们编译低级代码,相信我们做错了。 Still if humans make a mistake, it is caught at runtime.尽管如此,如果人类犯了错误,它会在运行时被捕获。

Consider the below example考虑下面的例子

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");
}
}

here we create the object of subclass Bone and assigned it to super class AOne reference and now superclass reference does not know about the method method2 in the subclass ie Bone during compile time.therefore we need to downcast this reference of superclass to subclass reference so as the resultant reference can know about the presence of methods in the subclass ie Bone这里我们创建了子类 Bone 的对象并将其分配给超类 AOne 引用,现在超类引用在编译时不知道子类中的方法 method2,即 Bone。因此我们需要将此超类引用向下转换为子类引用,以便结果引用可以知道子类中方法的存在,即 Bone

Downcasting transformation of objects is not possible.对象的向下转换是不可能的。 Only仅有的

DownCasting1 _downCasting1 = (DownCasting1)((DownCasting2)downCasting1);

is posible是可能的

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();
        }
    }

}

To do downcasting in Java, and avoid run-time exceptions, take a reference of the following code:要在 Java 中进行向下转换并避免运行时异常,请参考以下代码:

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

Here, Animal is the parent class and Dog is the child class.这里,Animal 是父类,Dog 是子类。
instanceof is a keyword that is used for checking if a reference variable is containing a given type of object reference or not. instanceof是一个关键字,用于检查引用变量是否包含给定类型的对象引用。

I will tell you why this happened.我会告诉你为什么会这样。 First of all you have to understand how JVM supports when we assign parent class into the child class using downcasting, because of reference .首先,您必须了解 JVM 在我们使用向下转换将父 class 分配给子 class 时如何支持,因为reference For example consider in the following code.例如考虑以下代码。

A is the super type any class that extends from it and can store the reference B class.
    A a =new B();
When you assign a reference variable into the child class jvm will understand that since A can store the reference of B class that is why you can do it.
B b=(B)b;
  1. The reason which is called compile time error and why you couldn't directly assign Parent class into the Child class because there is not any extends relationship.称为编译时错误的原因以及为什么不能直接将 Parent class 分配给 Child class 因为没有任何扩展关系。 Note that casting only occurring with the key which is called extends , that is why you receive the compile time error.请注意,转换仅发生在名为extends的键上,这就是您收到编译时错误的原因。
  2. Another reason which is called ClassCastException by the runtime because of jvm it directly accept the rule which is okay I accept that it is true but jvm after that will understand that by the runtime it is not store any referance of Child class when code was writing by the programmer who write coding in the syntax.运行时调用 ClassCastException 的另一个原因是因为 jvm 它直接接受规则,没关系,我接受它是真的,但 jvm 之后会明白,运行时它不会存储子 class 的任何引用,当代码由在语法中编写代码的程序员。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM