簡體   English   中英

Java的;將基類轉換為派生類

[英]Java; casting base class to derived class

為什么我不能將基類實例轉換為派生類?

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

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

或這個?

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){...}
}

現在我想創建一組擴展Base和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){
        ...
    }
}

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

如果可能的話,我真的很想在沒有泛型的情況下這樣做。


好吧,所以我最終搞清楚了,部分歸功於Maruice Perry的回答。

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

因為如果B擴展C,則意味着B是C而不是C是B.

重新思考你想要做什么。

現有答案在抽象論證方面很好,但我想做一個更具體的答案。 假設你可以那樣做。 然后這段代碼必須編譯並運行:

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

read方法的實現究竟在哪里? 它在InputStream是抽象的。 從哪里獲取數據? 將一個簡單的java.lang.Object視為InputStream 是不合適的。 對於演員而言拋出異常要好得多。

根據我的經驗,獲得“並行類層次結構”是很棘手的,就像你描述的那樣。 可能會發現仿制葯有所幫助,但它可以很快變得毛茸茸。

您需要使用instanceof關鍵字來檢查由n引用的對象的類型,並對該對象進行類型轉換並調用bar()方法。 下面是Checkout 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" );
    }
}

您可以為B創建一個構造函數,它將C作為參數。 請參閱此帖子 ,了解您正在嘗試做的事情。

基類不應該知道從它們派生的類的任何內容,否則將出現上面突出顯示的問題。 向下轉換是一種“代碼味道”,並且在基類中向衍生類的向下轉換特別“臭”。 這樣的設計也可能導致難以解決循環依賴性。

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

你不能這樣做,因為C不一定實現你在B中擴展它時創建的行為。

所以,假設C有一個方法foo() 然后你知道你可以在B上調用foo() ,因為B擴展了C,所以你可以相應地對B進行轉換,就好像它是一個帶有(C)(new B())

但是 - 如果B有一個方法bar() ,子類關系中的任何內容都表示你也可以在C上調用bar() 因此,您不能將C視為B,因此您無法投射。

在您的示例中,如果您確定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){
        ...
    }
}

因為如果B extends C ,那么B可能有不在C中的東西(比如在構造函數中初始化的實例變量不在新的C()中)

暫無
暫無

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

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