英文:
Why this java snippet code compile just fine?
问题
1.1 example的代码之所以能够编译通过,是因为Java中的泛型擦除机制(type erasure)会导致在编译时无法确定泛型参数的具体类型。因此,虽然在方法probablyIllegal
中传入了List<List<?>> lol
和List<?> list
,但在编译后,泛型信息会被擦除,编译器无法确定list
的具体类型。这也意味着,虽然在运行时可能会引发类型不匹配的问题,但在编译时不会报错。
1.2 example中的代码无法编译通过,因为在doSomethingIllegal
方法中,list1
和list2
都具有通配符泛型,编译器无法确定它们的具体类型,因此不允许直接使用list1.addAll(list2)
,以避免潜在的类型不匹配问题。
总的来说,1.1 example之所以能够编译通过是因为泛型擦除导致编译器无法在编译时检查类型,而1.2 example中编译器能够检测到类型不匹配,因此不允许编译通过。这是Java泛型系统的工作方式。
英文:
1.1 example
import java.util.*;
public class LOLUnknowns1 {
static void probablyIllegal(List<List<?>> lol, List<?> list) {
lol.add(list); // this compiles just fine
}
}
How come this 1.1 code compile if we may end up having List<List<Integer>> lol and a List<String> list?
this following code snippet:
1.2 example
import java.util.*;
public class TwoListsOfUnknowns2 {
static void doSomethingIllegal(List<?> list1, List<?> list2) {
list1.addAll(list2); // DOES NOT COMPILE as expected!!!
}
}
1.1 example baffles me, how is that possible to be compiled just fine?
I wnat to know the specific reason why a code compiled when it was expected not to from my understanding
答案1
得分: 2
以下是翻译的内容:
为什么这个1.1版本的代码可以编译通过,如果我们最终可能会有
List<List<Integer>> lol
和一个List<String> list
?
你不能将一个List<List<Integer>>
传递给类型为List<List<?>>
的参数。如果你尝试:
probablyIllegal(new ArrayList<List<Integer>>(), new ArrayList<String>());
你会得到以下错误:
在类型
LOLUnknowns1
中,方法probablyIllegal(List<List<?>>, List<?>)
不适用于参数(ArrayList<List<Integer>>, ArrayList<String>)
。
原因是即使List<Integer>
是List<?>
的子类型,但List<List<Integer>>
不是List<List<?>>
的子类型,就像String
虽然是Object
的子类型,但List<String>
不是List<Object>
的子类型一样。
如果你想能够传递一个List<List<Integer>>
,你需要将参数声明为List<? extends List<?>>
类型:
static void probablyIllegal(List<? extends List<?>> lol, List<?> list) {
lol.add(list);
}
在进行这个改变之后,编译器会接受你的调用,但会拒绝你的方法实现:
在类型
List<capture#1-of ?>
中,方法add(capture#1-of ? extends List<?>)
不适用于参数(List<capture#2-of ?>)
这个错误消息有点晦涩(你需要学习通配符捕获才能理解它),但基本上是说你不能将List<?>
添加到List<? extends List<?>>
中,因为你不知道lol
包含什么类型的元素,因此无法确定list
是否是兼容的类型。
总之,你可以为List
指定特定的元素类型,这样你可以add
,但不能使用不同类型的List
,或者你可以保留List
的元素类型未指定,这样你可以使用任何List
,但不能向其添加任何内容。无论哪种方式,都不会发生什么不好的事情。
英文:
> How come this 1.1 code compile if we may end up having List<List<Integer>> lol
and a List<String> list
?
You can not pass a List<List<Integer>>
to an argument of type List<List<?>>
. If you try:
probablyIllegal(new ArrayList<List<Integer>>(), new ArrayList<String>());
you get
> The method probablyIllegal(List<List<?>>, List<?>)
in the type LOLUnknowns1
is not applicable for the arguments (ArrayList<List<Integer>>, ArrayList<String>
).
The reason is that even though a List<Integer>
is a subtype of List<?>
, List<List<Integer>>
is not a subtype of List<List<?>>
, just like, even though String
is a subtype of Object
, List<String>
is not a subtype of List<Object>
.
If you want to be able to pass a List<List<Integer>>
, you need to declare the argument to be of type List<? extends List<?>>
:
static void probablyIllegal(List<? extends List<?>> lol, List<?> list) {
lol.add(list);
}
After that change, the compiler will accept your call, but reject your method implementation:
> The method add(capture#1-of ? extends List<?>)
in the type List<capture#1-of ? extends List<?>>
is not applicable for the arguments (List<capture#2-of ?>)
The error message is a bit cryptic (you need to read up on wildcard capture to understand it), but it basically says that you can't add the List<?>
to a List<? extends List<?>>
because you don't know what kind of elements lol
contains, and therefore can not know that list
is of a compatible type.
To conclude, you can either specify a specific element type for the List
, in which case you can add
, but not use a List
of a different type, or you can leave the element type of the List
unspecified, in which case you can use any List
, but not add
anything to it. Either way, nothing bad happens.
答案2
得分: 0
Re 1.1
只要你在泛型声明中不使用 extends
或 super
,编译器就无法在运行时决定是否会工作,因为只有 ?
告诉它:我想接受任何东西,而 add(E e)
也是如此。
你是否尝试使用具体的 List
对象作为 lol
、list
、list1
、list2
参数运行代码?
Re 1.2
错误(在这里是在 Eclipse 中)是:
> The method addAll(Collection<? extends capture#1-of ?>) in the type List<capture#1-of ?> is not applicable for the arguments (List<capture#2-of ?>)
因此,有两种不同的 通配符捕获。一个是 Collection<?>
(#1; addAll(Collection<? extends E> c)
预期的类型) 和一个是 List<?> list2
(#2)。
现在,请看上面第一个链接下的文章:
> 然而,Object 的集合不是任何集合的超类型。
>
> 例如,List<Object> 不是 List<String> 的超类型,[...]
你声明 list1
是一个任何东西 (?
<sub>1</sub>) 的 List
,因此 addAll()
预期的是扩展任何东西 (E
= ?
<sub>1</sub>) 的东西 (?
<sub>2</sub>),但是,请参考引用,泛型不是这样工作的。
英文:
Re 1.1
As long as you don't use extends
or super
in a generic declaration with wildcards the compiler can't decide whether this will work or not at runtime since with the sole ?
you tell it: I want to accept anything, and so does add(E e)
.
Have you tried running the code with concrete List
objects passed as lol
, list
, list1
, list2
arguments?
Re 1.2
The error (in Eclipse here) is:
> The method addAll(Collection<? extends capture#1-of ?>) in the type List<capture#1-of ?> is not applicable for the arguments (List<capture#2-of ?>)
So, there are two different wildcard captures. One of Collection<?>
(#1; expected by addAll(Collection<? extends E> c)
)/List<?> list1
and one of List<?> list2
(#2).
Now, see the article under the first link above:
> However, a collection of Object is not the supertype of any collection.
>
> For example, a List<Object> is not the supertype of List<String>, [...]
You declare list1
to be a List
of anything (?
<sub>1</sub>) hence addAll()
expects something (?
<sub>2</sub>) that extends anything (E
= ?
<sub>1</sub>) but, see the citation, that's not how generics work.
答案3
得分: 0
我认为这是你在寻找的内容。
要简要总结一下,对于第二个示例,如果你想要一个包含更多类型的列表,可以使用上界通配符。
在你的第一个示例中,有一个额外的包装器,List<?>
,这就是为什么它起作用的原因,最终使用了List接口中的一个方法。
英文:
I think this is what are you looking for.
To summarise a bit, for the second example you have can be done by a upper-bounded wildcard, if you want to have a list of more types.
In your first example you have an extra wrapper, the List<?>
this is why it works, in the end a method from the List interface is used.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论