簡體   English   中英

在Java返回類型中強制執行多個通用邊界

[英]Enforcing Multiple Generic Bounds in Java Return Type

這不是JavaFX的問題,但我正在嘗試在JavaFX中編寫一個接口來聲明一個可查看的類。 可查看的類意味着有一個view()方法,它返回一個表示Viewable的Node對象。 到目前為止簡單,但這里變得復雜。 應保證返回的Node具有getViewable()方法,該方法返回它所代表的Viewable對象。 我該如何做到這一點? 我的第一直覺是嘗試這樣的事情:

interface Viewable<V extends Viewable<V>>{
    <N extends Node&View<V>>N view();
}
interface View<V extends Viewable<V>>{
    V getViewable();
}

這首先出現聲音,並允許類如下所示:

class ViewableObject implements Viewable<ViewableObject>{
    @Override public ObjectView view(){
        return new ObjectView();
    }
    class ObjectView extends Pane implements View<ViewableObject>{
        @Override public ViewableObject getViewable(){
            return ViewableObject.this;
        }
    }
}

但是,出於某種原因,這個類還編譯:

class ViewableObject implements Viewable<ViewableObject>{
    @Override public Pane view(){
        return new Pane();
    }
}

Pane是一個Node,但它沒有實現View,為什么這個類會編譯? 我認為這違反了view()方法的約定。 更奇怪的是,當Pane被Object替換時,同一個類無法編譯:

class ViewableObject implements Viewable<ViewableObject>{
    @Override public Object view(){//complains this is not an @Override
        return new Object();
    }
}

這里發生了什么? 我對仿制葯的理解有缺陷嗎? 我怎樣才能讓它按預期工作?

在這種情況下,您不希望使用泛型方法,因為您的目標是修復view()的返回值類型。 通用方法允許調用者確定具體類型。 所以你真正做的就是強制執行。

我想你想要將Node類型的類型參數構建到接口定義中,這將強制view()返回正確的類型。 也許是這樣的:

interface Viewable<V extends Viewable<V, N>, N extends Node & View<V, N>> {
    N view();
}

interface View<V extends Viewable<V, TNode>, TNode extends Node & View<V, TNode>> {
    V getViewable();
}

class ViewableObject implements Viewable<ViewableObject, ViewableObject.ObjectView> {
    @Override
    public ObjectView view() {
        return new ObjectView();
    }

    class ObjectView extends Pane implements View<ViewableObject, ObjectView> {
        @Override
        public ViewableObject getViewable() {
            return ViewableObject.this;
        }
    }
}

如果查看Viewable.view()聲明的字節代碼,您將看到編譯器選擇第一個綁定的類型作為方法的實際返回類型。 以下是IntelliJ字節代碼查看器輸出的相關行:

// declaration: N view<N extends org.cumberlw.viewtest.Node, org.cumberlw.viewtest.View<V>>()
public abstract view()Lorg/cumberlw/viewtest/Node;

因此,當重寫時,您可以指定任何只與第一種類型協變的類型,編譯器將接受它。 如果切換類型邊界的順序,您將在字節代碼查看器中看到:

// declaration: N view<N extends org.cumberlw.viewtest.View<V>, org.cumberlw.viewtest.Node>()
public abstract view()Lorg/cumberlw/viewtest/View;

請注意,字節代碼表示返回值現在是View。 所以現在你的第二個例子將無法編譯,因為Pane不是View的子類。 參數的順序都不會讓第三個示例編譯,因為Object不是Node或View的子類。


使用具有多個邊界的泛型返回類型覆蓋方法也很容易產生運行時錯誤。 編譯器僅強制返回類型與第一個類型綁定協變,因此您可以返回不符合第二個類型綁定的類型。 例如,這編譯很好,但在運行時崩潰:

interface DogLike {
    void bark();
}

interface CatLike {
    void meow();
}

class Dog implements DogLike {
    @Override
    public void bark() {
        System.out.println("Woof");
    }
}

interface MoreauMachine {
    <H extends DogLike & CatLike > H createHybrid();
}

class MalfunctioningDogCatFactory implements MoreauMachine {

    @Override
    public DogLike createHybrid() {
        //Compile with -Xlint:unchecked to see a warning here:
        //Warning:(84, 20) java: createHybrid() in org.cumberlw.viewtest.MalfunctioningDogCatFactory implements <H>createHybrid() in org.cumberlw.viewtest.MoreauMachine
        //return type requires unchecked conversion from org.cumberlw.viewtest.DogLike to H
        return new Dog();
    }

    public static void main(String[] args) {
        MoreauMachine factory = new MalfunctioningDogCatFactory();

        //crashes!
        //Exception in thread "main" java.lang.ClassCastException: org.cumberlw.viewtest.Dog cannot be cast to org.cumberlw.viewtest.CatLike
        factory.createHybrid().meow();
    }
}

暫無
暫無

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

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