簡體   English   中英

向下轉換 Java

[英]Downcasting in Java

Java 中允許向上轉型,但是向下轉型會產生編譯錯誤。

可以通過添加強制轉換來消除編譯錯誤,但無論如何都會在運行時中斷。

在這種情況下,如果 Java 不能在運行時執行,為什么允許向下轉換?
這個概念有什么實際用途嗎?

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

當有可能在運行時成功時,向下轉換是允許的:

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 doesn't 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.

使用您的示例,您可以執行以下操作:

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

我相信這適用於所有靜態類型語言:

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

類型轉換有效地表示:假設這是對轉換類的引用並以此方式使用它。 現在,假設 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
}

我們都可以看到您提供的代碼在運行時不起作用。 那是因為我們知道表達式new A()永遠不可能B類型的對象。

但這不是編譯器的看法。 當編譯器檢查是否允許強制轉換時,它只會看到:

variable_of_type_B = (B)expression_of_type_A;

正如其他人所證明的那樣,這種類型的演員是完全合法的。 右邊的表達式可以很好地評估為類型B的對象。 編譯器看到AB具有子類型關系,因此使用代碼的“表達式”視圖,轉換可能會起作用。

當編譯器確切地知道expression_of_type_A將真正具有什么對象類型時,它不會考慮特殊情況。 它只是看到靜態類型為A ,並認為動態類型可以是A或任何后代A ,包括B

在這種情況下,如果 Java 不能在運行時執行,為什么它允許向下轉換?

我相信這是因為編譯器無法在編譯時知道轉換是否成功。 對於您的示例,很容易看出強制轉換將失敗,但在其他情況下則不太清楚。

例如,假設類型 B、C 和 D 都擴展了類型 A,然后方法public A getSomeA()根據隨機生成的數字返回 B、C 或 D 的實例。 編譯器無法知道此方法將返回哪種確切的運行時類型,因此如果稍后將結果轉換為B ,則無法知道轉換是否成功(或失敗)。 因此編譯器必須假設強制轉換會成功。

當我們處理向上轉換的對象時,向下轉換有效。 上行:

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

所以現在這個objValue變量總是可以向下轉換為int因為被轉換的對象是一個Integer

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

但是因為objValue是一個 Object 它不能轉換為String因為int不能轉換為String

@ 原始海報 - 見內嵌評論。

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

在我一直使用的以下代碼片段中,向下轉換非常有用。 因此證明向下轉換是有用的。

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

我將字符串存儲在鏈接列表中。 當我檢索鏈接列表的元素時,返回對象。 要將元素作為字符串(或任何其他類對象)訪問,向下轉換對我有幫助。

Java 允許我們編譯低級代碼,相信我們做錯了。 盡管如此,如果人類犯了錯誤,它會在運行時被捕獲。

考慮下面的例子

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 引用,現在超類引用在編譯時不知道子類中的方法 method2,即 Bone。因此我們需要將此超類引用向下轉換為子類引用,以便結果引用可以知道子類中方法的存在,即 Bone

對象的向下轉換是不可能的。 僅有的

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

}

要在 Java 中進行向下轉換並避免運行時異常,請參考以下代碼:

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

這里,Animal 是父類,Dog 是子類。
instanceof是一個關鍵字,用於檢查引用變量是否包含給定類型的對象引用。

我會告訴你為什么會這樣。 首先,您必須了解 JVM 在我們使用向下轉換將父 class 分配給子 class 時如何支持,因為reference 例如考慮以下代碼。

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. 稱為編譯時錯誤的原因以及為什么不能直接將 Parent class 分配給 Child class 因為沒有任何擴展關系。 請注意,轉換僅發生在名為extends的鍵上,這就是您收到編譯時錯誤的原因。
  2. 運行時調用 ClassCastException 的另一個原因是因為 jvm 它直接接受規則,沒關系,我接受它是真的,但 jvm 之后會明白,運行時它不會存儲子 class 的任何引用,當代碼由在語法中編寫代碼的程序員。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM