我正在阅读Java教程中的以下部分:http://docs.oracle.com/javase/tutorial/java/generics/capture.html
首先,由于捕获无法转换为Object而导致以下代码产生错误,因此set
方法无法确认Object是否为捕获类型#1:
import java.util.List;
public class WildcardError {
void foo(List<?> i) {
i.set(0, i.get(0));
}
}
我有点理解这背后的原因。 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));
}
}
我有点理解为什么这段代码也能正常工作,在这段代码中,l.get
保证是T类型,所以它可以作为T类型的参数传递。 / p>
我不明白为什么你不能使用这样的方法,没有帮助者:
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]
}
}
即。如果您要使用类型推断,为什么不在通配符和辅助函数上使用显式类型泛型?在这种情况下使用通配符有什么好处吗?在哪种情况下,您实际上更喜欢使用通配符而非类型通用?
答案 0 :(得分:1)
首先请注意,void foo(List<?> i)
和void <K> foo(List<K> i)
都接受完全相同的参数集 - 假设您没有在泛型方法案例中明确指定K
,那么任何参数都可以传递给一个函数签名可以传递给另一个,反之亦然。所以对于“外部代码”,两个签名都同样有用。
鉴于它们是等价的,具有较少类型参数的那个更简单,并且应该始终是首选的。在向外部代码提供公共API时,您应始终以最简单的形式呈现它,只有最小的类型才能使其安全。类型List<?>
足以表明它可以采用任何类型的List
,因此我们应该使用它。
我们碰巧在内部使用类型参数K
来解决一些泛型问题,但这只是一个不幸的内部实现细节,外部代码不需要关心。因此,我们应该隐藏这种丑陋,并在制作公共API时将其包装在更好的函数签名中。
除了抽象之外,您可能想要特定签名的另一个原因是它是否覆盖了具有该签名的超类方法。覆盖时无法添加类型参数。
答案 1 :(得分:0)
我认为您在教程中提到的示例是指当您需要接受带有未绑定通配符<?>
的参数时的处理案例。在与遗留代码交互或使用这种通配符类型扩展现有类时可能就是这种情况。
当您需要灵活性但不想牺牲类型检查时,您通常会使用<T extends U>
或<? extends T>
之类的内容。