4

假设我有一个这样的界面

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

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

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

4

2 回答 2

4

我认为这与您的特定设计无关。

您只是不能使用通配符实例化类型。

这也行不通:

return new ArrayList<?>();

如果确实如此,除了

return new ArrayList<Object>();

您需要一个具体的类,但这并不意味着您需要在工厂方法签名中显示它:

static <A,B> Foo<A,B> makeFoo(){
    return new FooImpl<A,B, SomeTypeThatNoOneNeedsToSee>();
}
于 2012-09-10T07:22:32.893 回答
1

Java 编译器努力做到不聪明。在很多情况下,它显然不需要某些信息来生成正确的代码,但它仍然会报错。当 Java 被发明时,全世界都看到 C 以草率的态度试图编译一切,无论多么危险或愚蠢。目标javac是确保人们不会轻易将自己射中脚

因此,它假设您提到了一个原因Arg2- 如果某些事情没有必要,您肯定不会将其放入代码中。

解决方案:

  1. 实现时分配一个类型Barclass FooImpl<Ret, Arg1> implements Foo<Ret, Arg>, Bar<Ret, Arg1, Object>
  2. 创建一个BetterFooImpl<Ret, Arg1> extends FooImpl<Ret, Arg1, Object>. 这对 API 的使用者隐藏了第三个类型参数。
  3. 尽量去掉第三个参数Bar
  4. 谨慎使用泛型。这可能令人惊讶,但在某些情况下泛型根本无法正常工作。这通常是因为细微的实现细节会导致@SuppressWarning注释的爆炸式增长。发生这种情况时,我会问 SO,当我找不到我能理解的解决方案时,我会删除泛型。编写可维护的代码比通过泛型迷宫找到聪明的方法更重要。
于 2012-09-10T07:32:52.380 回答