类型参数隐藏中的Java泛型

时间:2012-09-10 07:05:08

标签: java generics interface wildcard

假设我有一个像这样的界面

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中的BarFooImpl的实现,将两个接口声明的方法转发到一个varargs方法)

现在,假设在同一个包中的某个地方有一个静态方法,它将FooImpl作为Foo返回。有人会认为你可以使用return new FooImpl<Ret, Arg1, ?>(...),而事实上你不能(你必须使用具体的虚拟类型为Arg2,即。return new FooImpl<Ret, Arg1, Object>(...),比如说。

知道为什么会这样,尤其是因为Foo接口和包可见性有效地隐藏了FooImpl来自使用静态方法的内容?是不是因为人们仍然可以在某种程度上使用反射来到Bar FooImpl部分,那里需要具体的类型?

2 个答案:

答案 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是有原因的 - 如果没有必要,你肯定不会把它放入代码中。

解决方案:

  1. 在实施Bar时指定类型:class FooImpl<Ret, Arg1> implements Foo<Ret, Arg>, Bar<Ret, Arg1, Object>
  2. 创建BetterFooImpl<Ret, Arg1> extends FooImpl<Ret, Arg1, Object>。这隐藏了API消费者的第三种类型参数。
  3. 尝试摆脱Bar
  4. 的第三个参数
  5. 仔细使用泛型。这可能是令人惊讶的,但有些情况下,仿制药根本无法正常工作。这通常是因为细微的实现细节导致@SuppressWarning注释爆炸。当发生这种情况时,我问SO,当我找不到我能理解的解决方案时,我删除了泛型。编写可维护的代码比通过泛型迷宫找到聪明的方法更重要。