簡體   English   中英

Java繼承行為異常

[英]Java inheritance not behaving as expected

需要以下上下文:這種編碼方式的目的是避免if - else語句和instanceof 這總是一個壞主意。

我有3個帶有以下簽名的類:

abstract class A {}
class B extends A {}
class C extends A {}

然后我有另一個具有以下結構的類:

class MyClass {
    private final A model;

    public MyClass(A m) {
        this.model = m;
    }

    public void doSomething() {
        System.out.println(this.model instanceof C); //TRUE!!
        execute(this.model);
    }

    private void execute(A m) {
        System.out.println("noo");
    }

    private void execute(C m) {
        System.out.println("yay");
    }
}

最后是我主要內容:

public static void main(String... args) {
    C mod = new C();
    MyClass myClass = new MyClass(mod);
    myClass.doSomething();
}

現在的問題; execute(C)方法永遠不會執行,它始終是execute(A)方法。 我該如何解決? 我無法將execute(A)方法的簽名更改為execute(B),因為這會在MyClass#doSomething上提示Java“無法解析方法execute(A)”。

方法重載在編譯時解決。 在編譯時, m的類型為A ,因此execute(A m)被執行。

另外,私有方法是不可覆蓋的。

解決方案是使用@OliverCharlesworth建議的Visitor模式。

您的代碼說明了對象的靜態和動態類型之間的區別。 靜態類型是編譯器已知的。 動態類型是運行時實際存在的內容。

model字段的靜態類型為A

private final A model;

也就是說,編譯器知道A本身或其某些實現將被分配給model 編譯器一無所知,因此要在execute(A m)execute(C m)之間進行選擇時,唯一的選擇就是execute(A m) 該方法基於對象的靜態類型進行解析。

另一方面, instanceof了解動態類型。 它可以告訴您model已設置為C ,因此在打印輸出中報告了true

您可以通過向A添加方法並在BC覆蓋該方法以路由至適當的execute來解決此問題:

abstract class A {
    public abstract void callExecute(MyClass back);
}
class B extends A {
    public void callExecute(MyClass back) {
        back.execute(this);
    }
}
class C extends A {
    public void callExecute(MyClass back) {
        back.execute(this);
    }
}

class MyClass {
    private final A model;

    public MyClass(A m) {
        this.model = m;
    }

    public void doSomething() {
        System.out.println(this.model instanceof C); //TRUE!!
        model.callExecute(this.model);
    }

    public void execute(B m) {
        System.out.println("noo");
    }

    public void execute(C m) {
        System.out.println("yay");
    }
}

請注意,這兩個實現都調用

back.execute(this);

然而,內部的執行B具有this類型的B ,和內部執行C具有this類型的C ,因此呼叫被路由到的不同的重載execute的方法MyClass

我無法將execute(A)方法的簽名更改為execute(B)

還要注意,現在您也可以(並且應該)這樣做,因為基於this類型對正確的重載執行了回調。

方法重載是編譯時的多態性。 因此,要調用方法execute(C)您需要將模型定義為class C 最好在class A定義方法execute()並在子類中重寫它。

abstract class A {
    abstract void execute();
}
class B extends A {
    public void execute(){};
}
class C extends A {
    public void execute(){};
}

接着:

class MyClass {
    private final A model;

public void doSomething() {
    model.execute();
}

使用多態性來避免if-else語句和instanceof檢查的更好的方法

您正在將C類型的對象作為構造函數中A類型的對象發送(您已經完成了轉換),並將其分配給A類型的引用(這將導致僅調用execute(A)方法)。您可以檢查是否object是C的實例,並根據結果調用所需的方法。 你可以這樣

    public void doSomething(){
        System.out.println(model instanceof C);
        if (model instanceof C) execute((C)model);
        else
            execute(model);
    }

暫無
暫無

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

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