2010-04-03 260 views
15

爲什麼我無法將基類實例轉換爲派生類?Java;將基類轉換爲派生類

例如,如果我有一個擴展類C的類B,爲什麼我不能這樣做?

B b=(B)(new C()); 

or this?

C c=new C(); 
B b=(B)c; 

好吧,讓我更加具體,以什麼我想要做的事。下面是我有:

public class Base(){ 
    protected BaseNode n; 
    public void foo(BaseNode x){ 
     n.foo(x); 
    } 
} 


public class BaseNode(){ 
    public void foo(BaseNode x){...} 
} 

現在我想創建一個新的組類延伸基地和Basenode,像這樣:

public class Derived extends Base(){ 
    public void bar(DerivedNode x){ 
     n.bar(x);//problem is here - n doesn't have bar 
    } 
} 

public class DerivedNode extends BaseNode(){ 
    public void bar(BaseNode){ 
     ... 
    } 
} 

所以基本上我想新的功能添加到基地和BaseNode通過擴展它們並添加一個函數給它們。此外,Base和BaseNode應該可以獨立使用。

如果可能的話,我真的很喜歡不使用泛型。


好吧,所以我最終找出了答案,部分得益於Maruice Perry的回答。

在我的構造函數Base中,n被實例化爲BaseNode。我所要做的只是在構造函數的派生類中將n重新實例化爲DerivedNode,它完美地工作。

+0

首先,你需要使BaseNode n;受到保護,以便派生可以看到它。 – Joel 2010-04-03 19:30:18

+0

Woops(改變它)。 – Cam 2010-04-03 19:31:23

+0

我的意思是沒有任何方式沒有泛型這樣做?就像我說過的,我希望基類可以按原樣使用 - 添加泛型會使它們更加麻煩,並且會刪除Base應該由Base封裝的BaseNode類的封裝。 – Cam 2010-04-03 19:43:23

回答

6

您需要使用的instanceof關鍵字由ñ檢查對象的引用的類型和類型轉換對象,並調用欄()方法。結帳Derived.bar()方法波紋管

public class Test{ 
    public static void main(String[] args){ 
     DerivedNode dn = new DerivedNode(); 
     Derived d = new Derived(dn); 
     d.bar(dn); 
    } 
} 

class Base{ 
    protected BaseNode n; 
    public Base(BaseNode _n){ 
     this.n = _n; 
    } 

    public void foo(BaseNode x){ 
     n.foo(x); 
    } 
} 


class BaseNode{ 
    public void foo(BaseNode x){ 
     System.out.println("BaseNode foo"); 
    } 
} 

class Derived extends Base{ 
    public Derived(BaseNode n){ 
     super(n); 
    } 

    public void bar(DerivedNode x){ 
     if(n instanceof DerivedNode){ 
      // Type cast to DerivedNode to access bar 
      ((DerivedNode)n).bar(x); 
     } 
     else { 
      // Throw exception or what ever 
      throw new RuntimeException("Invalid Object Type"); 
     } 
    } 
} 

class DerivedNode extends BaseNode{ 
    public void bar(BaseNode b){ 
     System.out.println("DerivedNode bar"); 
    } 
} 
19

因爲如果B擴展C,這意味着B是一個C而不是C是B.

重新考慮你正在嘗試做的。

+0

我編輯了我的帖子,並添加了它實際上正在嘗試的內容。有任何想法嗎? – Cam 2010-04-03 19:26:06

2

你不能這樣做,因爲C並不一定實現,當你在B.

擴展它,你創建的行爲,所以,說C有一個方法foo()。然後你知道你可以在B上調用foo(),因爲B擴展了C,所以你可以相應地處理一個B,就好像它是一個C (C)(new B())

但是,如果B有一個方法bar(),子類關係中沒有任何內容說你可以在C上調用bar()。因此你不能把C當作B來對待,所以你不能投。

0

因爲如果B extends C,那麼B可能有東西,不C(像實例變量你在不在新的C構造函數初始化())

3

可以爲B中需要創建一個構造C作爲參數。 請參閱this post瞭解您想要做什麼的想法。

16

現有的答案在抽象論證方面沒有問題,但我想提出更具體的答案。假設你可以這樣做。然後這個代碼將不得不編譯和運行:

// Hypothetical code 
Object object = new Object(); 
InputStream stream = (InputStream) object; // No exception allowed? 
int firstByte = stream.read(); 

在哪裏執行read方法從哪裏來?它在InputStream中是抽象的。它從哪裏獲得數據?它只是不適合作爲一個InputStream對待裸java.lang.Object。演員投擲異常要好得多。

根據我的經驗,獲得像您所描述的工作類似的「並行類層次結構」非常棘手。你可能發現泛型幫助,但它可以很快得到毛。

+0

謝謝喬恩。經過一些額外的思考,這完全有道理。另一種解釋可能是這樣的:我們已經可以將派生類轉換爲基類 - 這意味着我們可以將任何東西投射到對象上。但是,如果我們可以將基類轉換爲派生類,那麼我們可以有效地將Object轉換爲任何東西。如果我們能夠做到這兩件事,那麼任何班級都可以上任何其他班級 - 這顯然沒有意義。 感謝您的幫助,並在'平行類hipedia'驗證是一個痛苦得到工作:) – Cam 2010-04-04 02:47:46

2

在你爲例,你可以投N轉換爲DerivedNode如果你確信n是DerivedNode的實例,也可以使用泛型:

public class Base<N extends BaseNode> { 
    protected N n; 
    public void foo(BaseNode x){ 
     n.foo(x); 
    } 
} 


public class BaseNode { 
    public void foo(BaseNode x){...} 
} 

public class Derived extends Base<DerivedNode> { 
    public void bar(DerivedNode x){ 
     n.bar(x); // no problem here - n DOES have bar 
    } 
} 

public class DerivedNode extends BaseNode { 
    public void bar(BaseNode){ 
     ... 
    } 
} 
+0

謝謝! 「在你的例子中,如果你確定n是DerivedNode的一個實例,你可以將n轉換成DerivedNode」非常有幫助! – Cam 2010-04-03 20:03:20

2

基類不應該知道由其衍生類的任何信息,否則上面強調會出現的問題。向下轉換是一種'代碼異味',並且在基類中向下派生成派生類特別「臭」。這樣的設計也可能導致難以解決循環依賴性。

如果您希望基類使用派生類實現,請使用Template方法模式,即在基類中添加虛擬或抽象方法,並在派生類中重寫並實現它。然後你可以從基類安全地調用它。