[英]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
的對象。 編譯器看到A
和B
具有子類型關系,因此使用代碼的“表達式”視圖,轉換可能會起作用。
當編譯器確切地知道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;
extends
的鍵上,這就是您收到編譯時錯誤的原因。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.