Java Generics - 列出<! - ? - > vs List <t> </t>

时间:2015-03-30 10:00:24

标签: java generics

考虑以下2个备用API:

void method(List<?> list)
<T> void method(List<T> list)

我知道他们的内部实现会有很多不同之处,例如List<?>无法写入列表等。

另外,据我所知,List<?>将允许任何带有List的参数化类型作为基本类型。 List<T>也是如此。

任何人都可以告诉我这两种API接受的输入类型是否存在任何差异。 (不是2个API内部实现差异。)

1 个答案:

答案 0 :(得分:3)

内部实现完全相同。事实上,使用javac编译的两个方法将产生相同的方法字节代码,如果它们完全编译的话。)

但是,在编译期间,第一个方法指定无关关于列表的组件类型,而第二个方法要求组件类型不变。这意味着每当我调用这样的方法时,list的组件类型将由调用站点使用的任何内容修复。

我可以使用List<String>调用我的方法,T在调用期间(从编译器的角度来看)将与String同义。我也可以使用List<Runnable>来呼叫它,T在通话期间与Runnable同义。

请注意,您的方法不返回任何内容,但根据参数,它可以很好地返回。考虑方法:

<T> T findFirst(Collection<T> ts, Predicate<T> p) { … }

您可以为每个T使用此方法。 但是它只有在我们的T对于集合和谓词相同时才有效 - 这就是“不变性”的含义。实际上,您可以指定适用于更多上下文的方法:

<T> T findFirst(Collection<? extends T> ts, Predicate<? super T> p) { … }

这种方法与上面的方法相同,但它接受的类型会更宽松。考虑类型层次结构A extends B extends C。然后你可以打电话:

Collection<A> cs = …;
Predicate<C> p = …;
B b = findFirst(cs, p);

我们调用ts 协变的类型和p的类型(在方法签名中)逆变

通配符(?)是另一回事。它们可以是有界的(如上面的情况)有界或逆变。如果它们是无界的,编译器实际上需要一个具体的类型来在编译时填写(这就是为什么你有时会得到像“通配符 - #15不匹配通配符 - #17”这样的错误的原因)。具体规则列于Java Language Specification, Section 4.5.1