繁体   English   中英

Java Generics - 列表 <?> vs List <T>

[英]Java Generics - List<?> vs List<T>

考虑以下2个备用API:

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

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

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

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

内部实现完全相同。 事实上,使用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语言规范的4.5.1节中列出。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM