假设我有一个像这样的界面
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
部分,那里需要具体的类型?
答案 0 :(得分:4)
我认为这与您的特定设计无关。
您无法使用通配符实例化类型。
这不起作用:
return new ArrayList<?>();
如果确实如此,除了
之外还会做什么return new ArrayList<Object>();
您需要一个具体的类,但这并不意味着您需要在工厂方法签名中显示它:
static <A,B> Foo<A,B> makeFoo(){
return new FooImpl<A,B, SomeTypeThatNoOneNeedsToSee>();
}
答案 1 :(得分:1)
Java编译器努力不聪明。有很多情况很明显,它不需要一些信息来产生正确的代码,但它仍然抱怨。当Java被发明时,无论多么危险或愚蠢,世界已经看到C以其邋attitude的态度尝试编译所有内容。 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,当我找不到我能理解的解决方案时,我删除了泛型。编写可维护的代码比通过泛型迷宫找到聪明的方法更重要。