【问题标题】:Overriding multiple generic functions with a single function with common upper bounds使用具有共同上限的单个函数覆盖多个泛型函数
【发布时间】:2020-04-18 07:05:28
【问题描述】:

我有两个不同的界面

interface ColumnSet {
   <V, I extends Column & Input<V>> V getValue(I column);
}

interface ParameterSet {
   <V, I extends Parameter & Input<V>> V getValue(I value);
}

ColumnParameter 类型只是简单的标记接口,用于阻止 Columns 在应使用 Parameters 的地方使用,反之亦然。因此,在幕后,我希望有一个单独的类来实现它们,如下所示:

class ObjectSet implements ColumnSet, ParameterSet {
   @Override public <V, I extends Input<V>> V getValue(I input) {
       ...
   }
}

从逻辑上讲,ObjectSet.getValue 似乎应该是ColumnSet.getValueParameterSet.getValue 的有效覆盖,因为它将任何Input&lt;V&gt; 作为参数,其上限为Column &amp; Input&lt;V&gt;Parameter &amp; Input&lt;V&gt;。然而,Java 9 并不认为它会覆盖其中任何一个报告 The method getValue() of type ObjectSet must override or implement a generic supertype method

这是 Java 中泛型的限制还是我遗漏了一些基本的东西?

(显然我无法在 ObjectSet 中创建两个单独的方法,因为它们具有相同的擦除,这让我可以选择为我正在尝试的接口中的两个 getValue 方法提供不同的名称避免)。

【问题讨论】:

  • 我通过在ObjectSet中添加方法&lt;V, I extends Column &amp; Input&lt;V&gt;&gt; V getValue(I column);&lt;V, I extends Column &amp; Input&lt;V&gt;&gt; V getValue(I column);来编译没有错误。
  • 啊,所以在我原来的例子中,我交换了Column/ParameterInput。如果你在任何地方都写了Column &amp; Input&lt;V&gt;Parameter &amp; Input&lt;V&gt;,那么你可以添加这两种方法。如果你改为在任何地方写 Input&lt;V&gt; &amp; Column 和 `Input & Parameter`,则不允许添加这两种方法,因为类型擦除只使用第一个绑定而不是第二个绑定。这给了我一个继续的方法,谢谢!我仍然对为什么原来的方法不起作用感兴趣。
  • 我刚刚意识到如何避免您的问题并将ObjectSetColumnSetParameterSet 以及所有其他可以创建的设置集解耦。看看我的答案,我已经更新了。

标签: java generics overriding upperbound


【解决方案1】:

根据(§8.4.8.1)如果m1 的签名是m2 签名的子签名(§8.4.2),则实例方法m1 会覆盖另一个实例方法m2

换句话说,重写方法签名应该与重写方法签名相同或擦除重写方法签名的(§4.6)

因此,被覆盖的方法参数不能用覆盖方法中不太具体的类型的参数替换。您可以阅读为什么会这样here


在您的情况下,ObjectSet#getValue 签名与 ColumnSet#getValue 签名及其擦除签名 (Object getValue(Column column)) 不同。 ParameterSet#getValue 也是如此。


正如@samabcde 所指出的,如果基方法是这样声明的:

<V, I extends Column & Input<V>> V getValue(I column);
<V, I extends Parameter & Input<V>> V getValue(I value);

您可以按如下方式实现它们:

@Override
public <V, I extends Column & Input<V>> V getValue(I value) {
    return doGetValue(value);
}

@Override
public <V, I extends Parameter & Input<V>> V getValue(I value) {
    return doGetValue(value);
}

private <V, I extends Input<V>> V doGetValue(I value) { ... }

如果基方法是这样声明的:

<V, I extends Input<V> & Column> V getValue(I value);
<V, I extends Input<V> & Parameter> V getValue(I value);

您只能按如下方式实现它们的擦除:

@Override
public Object getValue(Input value) { ... }

由于这些选项看起来都不太好,我建议您按照以下方式重新设计您的代码:

public interface Input<R, V> {
    // ...
}
public interface ObjectSet<R> {
    <V> V getValue(Input<R, V> input);
}
public class ObjectSetImpl<R> implements ObjectSet<R> {
    @Override
    public <V> V getValue(Input<R, V> input) {
        // ...
    }
}

现在您可以轻松创建仅接受带有特定类型参数RInputObjectSet 实例:

public interface Column<V> extends Input<Column<?>, V> {}
ObjectSet<Column<?>> columnSet = new ObjectSetImpl<>();

如果您不喜欢写 ObjectSet&lt;Column&lt;?&gt;&gt;,您可以创建一个命名更好的 ObjectSet&lt;Column&lt;?&gt;&gt; 实现,将所有工作委托给 ObjectSetImpl

public class ColumnSet extends DelegatingObjectSet<Column<?>> {}
ColumnSet columnSet = new ColumnSet();

DelegatingObjectSet 在哪里:

abstract class DelegatingObjectSet<R> implements ObjectSet<R> {
    // you can use dependency injection here
    private final ObjectSet<R> delegate = new ObjectSetImpl<>();

    @Override
    public <V> V getValue(Input<R, V> input) {
        return delegate.getValue(input);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-19
    • 1970-01-01
    相关资源
    最近更新 更多