[英]Java Generics Capture List<?>
I was looking at the Java Generics documentation and found this piece of code, 我正在查看Java Generics文档并找到这段代码,
public class WildcardError {
void foo(List<?> l) {
//This give a compile time error
l.set(0,l.get(0));
}
}
I can understand that we are fetching an element from a List<?>
and trying to set it to another List<?>
. 我可以理解我们从
List<?>
中获取一个元素并尝试将其设置为另一个List<?>
。 So the compiler gives an error. 所以编译器会出错。 My question is it makes sense when the 2 lists are different ie
l.set(0, m.get(0))
here lists l
and m
are different. 我的问题是当2个列表不同
l.set(0, m.get(0))
即l.set(0, m.get(0))
这里列出l
和m
是不同的。 But in the above example, l
and l
are the same lists. 但在上面的例子中,
l
和l
是相同的列表。 Why isn't the compiler smart enough to see that? 为什么编译器不够聪明才能看到它? Is it hard to implement it?
实施起来难吗?
Edit : I am aware that I can fix it by a helper method or by using T
instead of a ?
编辑 :我知道我可以通过辅助方法或使用
T
而不是?
来修复它?
. 。 Just wondering why compiler doesn't do it for me.
只是想知道为什么编译器不会为我做这件事。
In your specific case, you can explicitly fix this: 在您的具体情况下,您可以明确解决此问题:
public class WildcardError {
<T> void foo(List<T> l) {
// This will work
l.set(0, l.get(0));
}
}
Or if you don't want to change the original API, introduce a delegate helper method: 或者,如果您不想更改原始API,请引入委托帮助程序方法:
public class WildcardError {
void foo(List<?> l) {
foo0(l);
}
private <T> void foo0(List<T> l) {
// This will work
l.set(0, l.get(0));
}
}
Unfortunately, the compiler cannot infer that "obvious" <T>
type. 不幸的是,编译器无法推断出“明显的”
<T>
类型。 I've been wondering about that, too. 我一直在想这个。 It seems like something that could be improved in a compiler, as every wild card can be informally translated to an unknown
<T>
type. 它似乎可以在编译器中得到改进,因为每个通配符都可以非正式地转换为未知的
<T>
类型。 Probably, there are some reasons why this was omitted, perhaps this is only intuition, but formally impossible. 可能有一些原因可以解释为什么这个被省略,也许这只是直觉,但在形式上是不可能的。
UPDATE : 更新 :
Note, I've just seen this peculiar implementation of Collections.swap()
: 注意,我刚刚看到了
Collections.swap()
这种特殊实现:
public static void swap(List<?> list, int i, int j) {
final List l = list;
l.set(i, l.set(j, l.get(i)));
}
The JDK guys resort to a raw type, in order to handle this, locally. JDK家伙采用原始类型,以便在本地处理此问题。 This is a strong statement indicating that this probably should be supported by the compiler, but for some reason (eg lack of time to formally specify this) just wasn't done
这是一个强有力的声明,表明这可能应该由编译器支持,但由于某种原因(例如,没有时间正式指定这一点)只是没有完成
The compiler reports an error because there is no way -- in general -- that it can tell whether two expressions, (in this case l
and l
) refer to the same list. 编译器会报告错误,因为通常情况下它无法判断两个表达式(在本例中为
l
和l
)是否引用相同的列表。
Related, somewhat generalized, question: 相关的,有点概括的问题:
List<?>
means list containing elements of some unknown type , so when one wants to take elements from it using list.get(i)
it will return object of some unknown type , so the only valid guess will be Object
. List<?>
表示包含某些未知类型的元素的列表 ,因此当想要使用list.get(i)
获取元素时,它将返回某种未知类型的对象 ,因此唯一有效的猜测将是Object
。 Then when one tries to set element back using list.set(index, list.get(index))
it produces compile-time error, since as mentioned above List<?>
can only contain some unknown type , so putting Object
to it may cause ClassCastException
. 然后当使用
list.set(index, list.get(index))
尝试设置元素时list.set(index, list.get(index))
它会产生编译时错误,因为如上所述, List<?>
只能包含一些未知类型 ,因此将Object
放入其中可能导致ClassCastException
。
This is explained very well in Joshua Bloch's Effective Java, 2nd ed., Item 28: Use bounded wildcards to increase API flexibility Joshua Bloch的Effective Java,第2版,第28项:使用有界通配符增加API灵活性,这一点得到了很好的解释。
This is also known as PECS
principle and good explanation can be found in this Q/A: What is PECS (Producer Extends Consumer Super)? 这也称为
PECS
原理,在此Q / A中可以找到很好的解释: 什么是PECS(Producer扩展消费者超级)? (please note that List<?>
is the same as List<? extends Object>
with minor exceptions) (请注意,
List<?>
与List<? extends Object>
相同,但有少量例外)
In laymans terms, one should use List<?>
as method parameter only to get elements from it inside that method, not when one needs to put elements into list. 在非专业术语中,应该使用
List<?>
作为方法参数,仅在该方法中从中获取元素 ,而不是在需要将元素放入列表时。 When one needs to both put and get he/she needs to either generify method using type parameter T
as in Lukas Eder's answer (type-safe way) or simply use List<Object>
(not type-safe way). 当需要同时放置和获取他/她需要使用类型参数
T
生成方法时,如Lukas Eder的答案(类型安全方式)或简单地使用List<Object>
(不是类型安全方式)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.