在Java返回类型

时间:2015-09-02 00:53:16

标签: java generics

这不是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();
    }
}

这里发生了什么?我对仿制药的理解有缺陷吗?我怎样才能让它按预期工作?

1 个答案:

答案 0 :(得分:2)

在这种情况下,您不希望使用泛型方法,因为您的目标是修复view()的返回值类型。通用方法允许调用者确定具体类型。所以你真正做的就是强制执行。

我认为您希望将Node类型的类型参数构建到接口定义中,这将强制view()返回正确的类型。也许是这样的:

<script src="https://maps.googleapis.com/maps/api/js?libraries=places"></script>
<input id="pac-input" class="controls" type="text" placeholder="Search Box" style="display:none">
<div id="map"></div>

如果查看Viewable.view()声明的字节代码,您将看到编译器选择绑定的第一个类型作为方法的实际返回类型。以下是IntelliJ字节代码查看器输出的相关行:

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;
        }
    }
}

因此,当覆盖时,您可以指定任何仅与第一种类型协变的类型,编译器将接受它。如果切换类型边界的顺序,您将在字节代码查看器中看到:

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

请注意,字节代码表示返回值为View now。所以现在你的第二个例子将无法编译,因为Pane不是View的子类。参数的顺序都不会让第三个示例编译,因为Object不是Node或View的子类。

使用具有多个边界的泛型返回类型覆盖方法也很容易产生运行时错误。编译器仅强制返回类型与第一个类型绑定协变,因此您可以返回不符合第二个类型绑定的类型。例如,这编译很好,但在运行时崩溃:

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