2016-09-29 71 views
1

我想在java中的一些奇怪的泛型。在以下代码中,foo上的替代适用,但bar上的替代不适用。我不明白为什么。用怪异的泛型在java方法中覆盖错误

请注意,两个覆盖之间的唯一区别在于MB的约束。当覆盖foo时,MB延伸Alpha<B, MB>,它的工作原理。当覆盖barMB,延伸Beta<B, MB>并且它不起作用,即使Beta<B, MB>延伸Alpha<B, MB>

我认为MB extends Beta<B, MB>Beta<A, MA> extends Alpha<A, MA>意味着MB extends Alpha<B, MB>。那为什么它不工作?

import java.util.function.Function; 

abstract class Alpha<A, MA extends Alpha<A, MA>> { 
    abstract public <B, MB extends Alpha<B, MB>> MB foo(Function<A, B> f); 
    abstract public <B, MB extends Alpha<B, MB>> MB bar(Function<A, B> f); 
} 

class Beta<A, MA extends Beta<A, MA>> extends Alpha<A, MA> { 
    @Override public <B, MB extends Alpha<B, MB>> MB foo(Function<A, B> f) { return null; } 
    @Override public <B, MB extends Beta<B, MB>> MB bar(Function<A, B> f) { return null; } 
} 
+1

[这里是一个简化版本](http://ideone.com/j9QkOG)同样的问题。当您覆盖泛型方法时,您无法更改类型参数的边界。 – Radiodef

+1

相关http://stackoverflow.com/questions/23438813/cannot-override-generic-interface – Tunaki

+0

好的人,我现在明白了。总之,在改变泛型类型的边界时,你不能重写一个方法。期。无论您是否使用泛型类型和事件,无论如何使用它。 –

回答

2

不同的是,约束MB extends Beta<B, MB>是不一样的MB extends Alpha<B, MB>;所以它不会覆盖bar方法。

the error message says

Main.java:10: error: name clash: <B#1,MB#1>bar(Function<A#1,B#1>) in Beta and <B#2,MB#2>bar(Function<A#2,B#2>) in Alpha have the same erasure, yet neither overrides the other 
    @Override public <B, MB extends Beta<B, MB>> MB bar(Function<A, B> f) { return null; } 

所以,你需要改变你的通用约束,使Beta.bar确实覆盖Alpha.bar

+0

谢谢。我明白这不是一个约束。但是MB中的限制更具体。它被用作返回类型。所以,Beta.bar无法返回任何与Alpha.bar不兼容的值。所以它应该没问题。 –

+0

好的,我接受了你的答案,因为它是最接近回答问题的答案。也许你应该更清楚地知道,如何使用泛型类型并不重要。 –

1

我预计MB扩展测试版< B,MB >和Beta < A,MA >延伸阿尔法< A,MA >将意味着MB扩展阿尔法< B,MB >。

虽然上述说法是正确的,所有MB扩展Beta<A, MB>还扩展Alpha<A, MB>,这不是问题。这两个方法签名

abstract public <B, MB extends Alpha<B, MB>> MB bar(Function<A, B> f); 

public <B, MB extends Beta<B, MB>> MB bar(Function<A, B> f); 

其实都是不同的!这些方法将接受不同的参数。假设我有一个class Gamma<A, MA extends Gamma<B, Gamma>> extends Alpha<A, MA>。根据抽象类Alpha中的规范,bar可能会返回类型为Gamma的对象,因为它是Alpha的子类。但是在barbarBeta的不能返回Gamma的类型,因为Gamma不是Beta的子类。因此,这两个方法签名的行为实际上会有所不同,从而使您在栏上的覆盖无效!

+0

但是Beta.bar没有义务返回Alpha.bar所能包含的内容。所以它不应该使覆盖无效。您可以用一个新的实现覆盖一个方法,该方法返回一个更具体的类型。 –

+0

我明白你的观点。我的猜测是,无论只影响返回参数的泛型参数,由于类型擦除Java不能允许泛型在覆盖过程中被缩小,因为它可能会影响其他不能缩小的类型(即参数或某些东西功能)。 –

+1

你有正确的想法,但它不是在这里有问题的返回类型。 Covariant返回类型在Java中很好(参见[例如这里](http://stackoverflow.com/a/1882587/2891664))。问题是特别的类型参数声明。 – Radiodef