这个Java代码片段为什么可以顺利编译?

huangapple go评论58阅读模式
英文:

Why this java snippet code compile just fine?

问题

1.1 example的代码之所以能够编译通过,是因为Java中的泛型擦除机制(type erasure)会导致在编译时无法确定泛型参数的具体类型。因此,虽然在方法probablyIllegal中传入了List<List<?>> lolList<?> list,但在编译后,泛型信息会被擦除,编译器无法确定list的具体类型。这也意味着,虽然在运行时可能会引发类型不匹配的问题,但在编译时不会报错。

1.2 example中的代码无法编译通过,因为在doSomethingIllegal方法中,list1list2都具有通配符泛型,编译器无法确定它们的具体类型,因此不允许直接使用list1.addAll(list2),以避免潜在的类型不匹配问题。

总的来说,1.1 example之所以能够编译通过是因为泛型擦除导致编译器无法在编译时检查类型,而1.2 example中编译器能够检测到类型不匹配,因此不允许编译通过。这是Java泛型系统的工作方式。

英文:

1.1 example

import java.util.*;

public class LOLUnknowns1 {
    static void probablyIllegal(List&lt;List&lt;?&gt;&gt; lol, List&lt;?&gt; list) {
        lol.add(list); // this compiles just fine
    }
}

How come this 1.1 code compile if we may end up having List&lt;List&lt;Integer&gt;&gt; lol and a List&lt;String&gt; list?

this following code snippet:
1.2 example

import java.util.*;

public class TwoListsOfUnknowns2 {
    static void doSomethingIllegal(List&lt;?&gt; list1, List&lt;?&gt; 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&lt;List&lt;Integer&gt;&gt; lol and a List&lt;String&gt; list?

You can not pass a List&lt;List&lt;Integer&gt;&gt; to an argument of type List&lt;List&lt;?&gt;&gt;. If you try:

probablyIllegal(new ArrayList&lt;List&lt;Integer&gt;&gt;(), new ArrayList&lt;String&gt;());

you get

> The method probablyIllegal(List&lt;List&lt;?&gt;&gt;, List&lt;?&gt;) in the type LOLUnknowns1 is not applicable for the arguments (ArrayList&lt;List&lt;Integer&gt;&gt;, ArrayList&lt;String&gt;).

The reason is that even though a List&lt;Integer&gt; is a subtype of List&lt;?&gt;, List&lt;List&lt;Integer&gt;&gt; is not a subtype of List&lt;List&lt;?&gt;&gt;, just like, even though String is a subtype of Object, List&lt;String&gt; is not a subtype of List&lt;Object&gt;.

If you want to be able to pass a List&lt;List&lt;Integer&gt;&gt;, you need to declare the argument to be of type List&lt;? extends List&lt;?&gt;&gt;:

static void probablyIllegal(List&lt;? extends List&lt;?&gt;&gt; lol, List&lt;?&gt; 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&lt;?&gt;) in the type List&lt;capture#1-of ? extends List&lt;?&gt;&gt; is not applicable for the arguments (List&lt;capture#2-of ?&gt;)

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&lt;?&gt; to a List&lt;? extends List&lt;?&gt;&gt; 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

只要你在泛型声明中不使用 extendssuper,编译器就无法在运行时决定是否会工作,因为只有 ? 告诉它:我想接受任何东西,而 add(E e) 也是如此。

你是否尝试使用具体的 List 对象作为 lollistlist1list2 参数运行代码?

Re 1.2

错误(在这里是在 Eclipse 中)是:

> The method addAll(Collection&lt;? extends capture#1-of ?>) in the type List<capture#1-of ?> is not applicable for the arguments (List<capture#2-of ?>)

因此,有两种不同的 通配符捕获。一个是 Collection&lt;?&gt; (#1; addAll(Collection&lt;? extends E&gt; c) 预期的类型) 和一个是 List&lt;?&gt; list2 (#2)。

现在,请看上面第一个链接下的文章:

> 然而,Object 的集合不是任何集合的超类型。
>
> 例如,List&lt;Object> 不是 List&lt;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&lt;? 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&lt;?&gt; (#1; expected by addAll(Collection&lt;? extends E&gt; c))/List&lt;?&gt; list1 and one of List&lt;?&gt; 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&lt;Object> is not the supertype of List&lt;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&lt;?&gt;,这就是为什么它起作用的原因,最终使用了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&lt;?&gt; this is why it works, in the end a method from the List interface is used.

huangapple
  • 本文由 发表于 2023年6月12日 03:06:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76452091.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定