假设我有一个像这样的界面
public interface Foo<Ret, Arg> {
public Ret doSomething(Arg arg);
}
还有一个仅具有包可见性的具体实现类。
class FooImpl<Ret, Arg1, Arg2> implements Foo<Ret, Arg>, Bar<Ret, Arg1, Arg2> {
// This class also implements something else making use of Arg2, so you cannot safely remove it
// But nothing relating to Arg2 is needed to create a FooImpl
}
(如何发生这种情况的一个示例是在
Foo
中实现Bar
和FooImpl
将两个接口声明的方法转发给一个varargs方法)现在,假设在同一包中的某处有一个静态方法,该方法返回
FooImpl
作为Foo
。有人认为您可以使用return new FooImpl<Ret, Arg1, ?>(...)
,而实际上却不能(您必须使用具体的虚拟类型作为Arg2
,即return new FooImpl<Ret, Arg1, Object>(...)
)。知道为什么会这样吗,特别是因为
Foo
接口和程序包可见性有效地将FooImpl
隐藏在使用静态方法的任何地方?是否因为这样一个事实,即在需要具体类型的地方,仍然可以在某种程度上使用反射来到达Bar
的FooImpl
部分? 最佳答案
Java编译器尽力不变得聪明。在很多情况下,很明显不需要某些信息即可生成正确的代码,但仍然会抱怨。发明Java时,无论C多么危险或愚蠢,世界都以C的草率态度来尝试编译所有内容。 javac
的目标是确保人们不能shoot themselves into the foot easily。
因此,它假定您提到Arg2
是有原因的-如果不需要某些内容,那么您肯定不会将其放入代码中。
解决方案:
Bar
时分配一种类型:class FooImpl<Ret, Arg1> implements Foo<Ret, Arg>, Bar<Ret, Arg1, Object>
BetterFooImpl<Ret, Arg1> extends FooImpl<Ret, Arg1, Object>
。这对API的使用者隐藏了第三种类型的参数。 Bar
的第三个参数@SuppressWarning
注释激增。发生这种情况时,我会询问SO,而当我找不到我能理解的解决方案时,便会删除泛型。编写可维护的代码比通过泛型迷宫找到巧妙的方法更为重要。