为什么我应该优先使用通配符而不是类型参数?

Why should I use wildcards in preference to type parameters?

我正在阅读 Effective Java 第 2 版,在 Item 28: Use bounded wildcards to increase API flexibility 中有以下内容:

// Two possible declarations for the swap method

public static <E> void swap(List<E> list, int i, int j);
public static void swap(List<?> list, int i, int j);

Which of these two declarations is preferable, and why? In a public API, the second is better because it’s simpler. You pass in a list—any list—and the method swaps the indexed elements. There is no type parameter to worry about. As a rule, if a type parameter appears only once in a method declaration, replace it with a wildcard.

我使用无界类型参数对其进行了测试,我没有看到任何缺点,我可以传递任何列表并且类型参数没有问题,实际上无界类型参数更好,因为我不需要序列中解释的辅助方法:

there is a way to implement this method without resorting to an unsafe cast or a raw type. The idea is to write a private helper method to capture the wildcard type. The helper method must be a generic method in order to capture the type. Here’s how it looks:

public static void swap(List<?> list, int i, int j) {
    swapHelper(list, i, j);
}
// Private helper method for wildcard capture
private static <E> void swapHelper(List<E> list, int i, int j) {
    list.set(i, list.set(j, list.get(i)));
}

最后,这是我使用类型参数的实现:

 public static <E> void swap(List<E> list, int i, int j) {
        list.set(i, list.set(j, list.get(i)));
  }

和用法:

List<Object> integers = (...)
swap(integers, 1,2);

那么,我为什么要改用通配符呢?

问题是:为什么第二种方式更简单?我不明白为什么!我错过了一些细节?好想明白布洛赫是什么意思

首先,了解 Bloch 在这里提出了关于 偏好 的主张 - 你不一定非要同意他的观点(而且我目睹了长期而艰苦的讨论这一点,所以你不是唯一的。

不过,他确实阐明了为什么他认为这样更好。我将尝试提取他的要点,使它们更清晰。

In a public API, the second is better because ... there is no type parameter to worry about. As a rule, if a type parameter appears only once in a method declaration, replace it with a wildcard....

This slightly convoluted implementation ... allows us to export the nice wildcard-based declaration....

Using wildcard types in your APIs, while tricky, makes the APIs far more flexible.

换句话说,Bloch 认为需要 E 类型是您不应该向客户端公开的实现细节 - swap() 方法应该适用于任何 List因此 ? 是“正确的”并且公开 E 对用户没有任何好处。从某种意义上说,这是 条款 52 的概括:通过接口引用对象

请特别注意,此建议仅针对 public API。没有必要在代码中实现此解决方法,只有您会调用。

这个例子也可以说是人为的 - 两种解决方案之间确实没有太大区别,但希望您可以想象更复杂的方法需要泛型作为其实现的一部分,而不是 public 签名。 Bloch 的一般观点是,当用户不需要了解泛型类型时,您无需将这些知识强加给他们,即使这样做看起来微不足道。