为什么我应该优先使用通配符而不是类型参数?
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 的一般观点是,当用户不需要了解泛型类型时,您无需将这些知识强加给他们,即使这样做看起来微不足道。
我正在阅读 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 的一般观点是,当用户不需要了解泛型类型时,您无需将这些知识强加给他们,即使这样做看起来微不足道。