我正在阅读Java教程中的以下部分:http://docs.oracle.com/javase/tutorial/java/generics/capture.html
它首先说下面的代码产生错误,因为捕获无法转换为Object,因此该set
方法无法确认Object是否为capture#1类型:
import java.util.List;
public class WildcardError {
void foo(List<?> i) {
i.set(0, i.get(0));
}
}
Run Code Online (Sandbox Code Playgroud)
我有点理解这背后的原因.i.get返回一个Object,编译器无法确定该对象是否为capture#1类型,因此无法以类型安全的方式将其与第二个参数匹配.
然后,它建议使用以下代码使此方法有效:
public class WildcardFixed {
void foo(List<?> i) {
fooHelper(i);
}
// Helper method created so that the wildcard can be captured
// through type inference.
private <T> void fooHelper(List<T> l) {
l.set(0, l.get(0));
}
}
Run Code Online (Sandbox Code Playgroud)
我有点理解为什么这段代码也适用,在这段代码中,l.get
保证是T类型的,所以它可以作为T类型的参数传递.
我不明白为什么你不能只使用这样的方法,没有帮助:
class GenericsTest {
static <K> void bar(List<K> l) {
l.set(0, l.get(l.size() - 1));
}
public static void main(String[] args) {
List<Integer> lst = Arrays.asList(1, 2, 3, 4);
bar(lst);
System.out.println(lst); // [4, 3, 2, 4]
}
}
Run Code Online (Sandbox Code Playgroud)
即如果您要使用类型推断,为什么不在通配符和辅助函数上使用显式类型泛型呢?在这种情况下使用通配符有什么好处吗?在哪种情况下,您实际上更喜欢使用通配符而不是类型通用?
首先请注意,void foo(List<?> i)
和void <K> foo(List<K> i)
两者都接受完全相同的参数集 - 假设您没有K
在泛型方法情况下显式指定 a ,则可以传递给一个函数签名的任何参数都可以传递给另一个函数签名,反之亦然。因此对于“外部代码”来说,这两个签名同样有用。
鉴于它们是等效的,类型参数较少的那个更简单,并且应该始终是首选。当向外部代码提供公共 API 时,您应该始终以最简单的形式呈现它,只包含确保其安全所需的最少类型内容。该类型List<?>
足以表达它可以采用任何类型List
,因此我们应该使用它。
我们碰巧K
在内部使用类型参数来解决一些泛型问题,但这只是一个不幸的内部实现细节,外部代码不需要关心。因此,在制作公共 API 时,我们应该隐藏这种丑陋之处并将其包装在更好的函数签名中。
除了抽象目的之外,您可能需要特定签名的另一个原因是它是否覆盖具有该签名的超类方法。覆盖时不能添加类型参数。