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

2022-09-01 12:02:47

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

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

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

此外,是否可以说泛型通配符类型仅在方法的参数声明时才有意义?


答案 1

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

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

以下是来自 Effective Java Item 28 的引用:

不要使用通配符类型作为返回类型。它不会为用户提供额外的灵活性,而是会强制他们在客户端代码中使用通配符类型。
如果使用得当,通配符类型对类的用户几乎不可见。它们使方法接受它们应该接受的参数,并拒绝那些它们应该拒绝的参数。如果类的用户必须考虑通配符类型,则该类的 API 可能存在问题。


答案 2

不,这样说是不可行的。

或者这样说:拥有这样的接口确实有意义。

想象一下

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();

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

是否希望具有这样的返回类型取决于应用情况。在某些情况下,它可能只是必要的。但是,如果它很容易避免,那么您可以这样做。


编辑:编辑代码以指出差异,即您可能并不总是能够在内部选择类型。但是,在大多数情况下,可以避免返回涉及通配符的内容 - 正如我所说,如果可能的话,应该避免。

上面概述的示例仍应被视为示例,以强调关键点。当然,尽管这样的实现将是一种不好的做法,因为它暴露了内部状态。

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

return Collections.<Number>unmodifiableList(integers);

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


推荐