2010-08-27 99 views
16
public class InterfaceCasting { 

    private static class A{} 

    public static void main(String[] args) { 
     A a = new A(); 
     Serializable serializable = new Serializable(){}; 
     a = (A)serializable; 
    } 

} 

編譯成功,但運行時異常Java的鑄造接口類

Exception in thread "main" java.lang.ClassCastException: InterfaceCasting$1 cannot be cast to InterfaceCasting$A 

爲什麼彙編成功嗎?編譯器必須知道serialiazable不是A?

+0

我認爲你必須審查Java異常的概念...... – ultrajohn 2010-08-27 09:30:23

回答

-1

Serializable不是A,所以它引發ClassCastException

+3

這是正確的,但不是OP所要求的。 – cherouvim 2010-08-27 09:32:10

0

它不知道,因爲編譯時間類型serializableSerializable

爲了說明,考慮一下:

private static class A{} 
private static class B implements Serializable {} 

Serializable serializable = new B(); 
A a = (A)serializable; 

這正是你的問題一樣,它編譯。

private static class A{} 
private static class B implements Serializable {} 

B b = new B(); 
A a = (A)b; 

這並不編譯,因爲b不是A

27

正如你指出,這編譯:

interface MyInterface {} 

class A {} 

public class InterfaceCasting { 
    public static void main(String[] args) { 
     MyInterface myObject = new MyInterface() {}; 
     A a = (A) myObject; 
    } 
} 

但是,將編譯:

interface MyInterface {} 

class A {} 

public class InterfaceCasting { 
    public static void main(String[] args) { 
     A a = (A) new MyInterface() {}; // javac says: "inconvertible types!" 
    } 
} 

那麼,這是怎麼回事嗎?有什麼不同?

好吧,既然MyInterface是一個簡單的接口,它可能非常以及由延伸的一類,在這種情況下,從MyInterfaceA投將法律實施。


此代碼例如,將在所有執行 50%的成功,說明了編譯器將需要解決,以始終「檢測」非法強制轉換在編譯時可能不可判定問題。

interface MyInterface {} 

class A {} 

class B extends A implements MyInterface {} 

public class InterfaceCasting { 
    public static void main(String[] args) { 
     MyInterface myObject = new MyInterface() {}; 
     if (java.lang.Math.random() > 0.5) 
      myObject = new B(); 
     A a = (A) myObject; 
    } 
} 
+0

這也可能不擴展A?因爲那是我需要的。 – Ben 2012-05-10 11:27:32

4
Serializable serializable; 
a = (A)serializable; 

至於編譯器,可變序列可包含實現Serializable任何對象,其包括A子類。所以它假設你知道變量確實包含一個A對象並允許該行。

+0

不,它可能不**包含'A'。編譯器確切地知道'A'沒有實現'Serializable'。 – aioobe 2010-08-27 09:38:42

+0

@aioobe是的,但是'A'的子類可能爲 – 2010-08-27 09:52:28

+0

,您應該寫下「其中包含A的子類」來澄清這一點。 – aioobe 2010-08-27 09:58:51

1

編譯器不夠聰明,無法追查serializable的來源,並意識到它永遠不可能是A類型。這真的只是評估行:

a = (A)serializable; 

並看到serializableSerializable類型的參考,但它可能會引用一個類,A類型。直到運行時才知道serializable引用的實際類。

在這個微不足道的案例中,我們知道這個轉換將永遠不會成功,但總的來說,這會留作運行時問題,因爲可能導致轉換的不同代碼路徑在理論上是無限的。

如果你想避免在運行時這個問題,你可以測試它..

if (serializable instanceof A) { 
    a = (A)serializable; 
} else .... 
0

雖然我不知道正確的答案,它通常不是投的接口是個好主意一個班,有幾個原因。

a)接口定義了一個合約,它保證了行爲。一個類可能定義了多於這個契約,其他方法的使用可能會有意想不到的副作用和中斷API。例如。當一個方法傳遞一個列表,你發現傳遞的對象實際上是一個LinkedList並且你投它並使用它也定義的基於隊列的方法時,你正在破壞API。 b)此外,帶接口的對象在運行時可能不是「真實」對象,但可能是由諸如Spring或EJB之類的庫在原始對象周圍創建的服務代理。在這些情況下你的演員陣容將失敗。

如果你絕對必須投,從來沒有一個instanceof檢查做到這一點:

if(myServiceObject instanceof MyServiceObjectImpl){ 
    MyServiceObjectImpl impl = (MyServiceObjectImpl) myServiceObject; 
} 
0

的編譯時引用類型S的一個值的鑄件轉換的編譯時合法性的詳細規則編譯時參考類型T如下:
[...]
如果S是接口類型:
- 如果T是數組類型,[...]。
- 如果T是一個不是最終的類型(§8.1.1),那麼如果存在T的超類型X和S的超類型Y,使得X和Y都是可證明的不同的參數化類型,並且X和Y的擦除是相同的,會發生編譯時錯誤。否則,在編譯時(因爲即使T沒有實現S,T的一個子類可能),轉換總是合法的。

來源:
JLS : Conversions and Promotions

8

Java language specification狀態,即:

一些石膏可以證明在編譯時不正確;這樣的轉換會導致編譯時錯誤。

後來在節目爲編譯時引用類型S的編譯時間基準T型的值的鑄造轉換的編譯時合法性的詳細規則 - 要注意,它們是非常複雜並且很難理解。

有趣規則是:

  • 如果小號接口類型:
    • 如果Ť是一類是不最終(§8.1.1) ,那麼如果存在T的超類型X和S的超類型Y,使得X和Y都是可證明不同的參數化類型,並且X和Y的刪除是相同的,則編譯時錯誤o ccurs。 否則,轉換在編譯時總是合法的(因爲即使T沒有實現S,也可能是T的一個子類)

在你的榜樣,這是完全明確的,即投是非法的。但考慮這個細微的變化:

public class InterfaceCasting { 

    private static class A{} 
    private static class B extends A implements Serializable{} 

    public static void main(String[] args) { 
     A a = new A(); 
     Serializable serializable = new B(){}; 
     a = (A)serializable; 
    }  
} 

現在從SerializableA鑄造可以在運行時,這表明,在這種情況下,最好還是留給運行時決定,如果我們可以投與否。

+0

是的,我現在得到它 謝謝大家的幫助 – komenan 2010-08-29 18:24:58