通用通配符类型不应在返回参数中使用

jil*_*t3d 20 java generics

是否可以说通用通配符类型不应该用在方法的返回参数中?

换句话说,声明一个如下所示的接口是有意义的:

interface Foo<T> {
  Collection<? extends T> next();
}
Run Code Online (Sandbox Code Playgroud)

另外,可以说通用通配符类型仅在方法的参数声明中有意义吗?

Roh*_*ain 21

使用通配符类型的主要好处,比如方法形式参数,是为用户提供灵活性来传递,比如任何类型的Collection,List或者任何实现Collection的东西(假设集合被声明为Collection<?>).您经常会发现自己在形式参数中使用通配符类型.

但理想情况下,您应该避免将它们用作方法的返回类型.因为这样,您将强制该方法的用户在调用者端使用通配符类型,即使他们不想这样做.通过使用通配符类型,你说,嘿!这种方法可以返回任何类型的Collection,所以你的工作就是照顾它.你不应该这样做.最好使用有界类型参数.使用有界类型参数,将根据您传递的类型或方法调用的目标类型推断类型.

以下是Effective Java Item 28的引用:

不要将通配符类型用作返回类型.它不会为您的用户提供额外的灵活性,而是迫使他们在客户端代码中使用通配符类型.
正确使用的通配符类型对于类的用户几乎是不可见的.它们使方法接受它们应该接受的参数并拒绝它们应该拒绝的参数.如果类的用户必须考虑通配符类型,那么类的API可能有问题.

  • @ jilt3d关于"不变性"(我在回答的编辑中也提到过):关于向集合中添加新元素的可能性,这是正确的.但它通常是**NOT**:例如,你仍然可以添加`null`,或者简单地在返回的集合上调用`collection.clear()`. (2认同)

Mar*_*o13 5

不,这样说是不可行的。

或者这样说:拥有这样的界面确实有意义。

想象以下

interface Foo<T>  
{
    Collection<? extends T> next();
}

class FooInteger implements Foo<Number> 
{
    private final List<Integer> integers = new ArrayList<Integer>();
    void useInternally()
    {
        integers.add(123);
        Integer i = integers.get(0);
    }

    @Override
    public Collection<? extends Number> next() 
    { 
        return integers;
    }
}

// Using it:
Foo<Number> foo = new FooInteger();
Collection<? extends Number> next = foo.next();
Number n = next.iterator().next();
Run Code Online (Sandbox Code Playgroud)

如果将返回类型编写为Collection<T>,则无法返回包含子类型 的集合T

是否需要这样的返回类型取决于应用程序案例。在某些情况下,它可能只是必要的。但如果很容易避免,那么你可以这样做。


编辑:编辑代码以指出不同之处,即您可能并不总是能够在内部选择类型。然而,在大多数情况下,返回的东西,包括一个通配符可以被避免的-正如我说的,如果可能的话,应尽量避免。

上面勾画的例子仍应被视为强调关键点的例子。虽然,当然,这样的实现将是一种不好的做法,因为它暴露了一个内部状态。

在这种和类似的情况下,人们通常可以返回类似

return Collections.<Number>unmodifiableList(integers);
Run Code Online (Sandbox Code Playgroud)

通过这种方式,将返回类型声明为Colletion<Number>: 该unmodifiableList方法解决了公开内部状态的问题,并具有允许将类型参数更改为超类型的简洁属性,因为该列表是……好吧,无论如何都是不可修改的。