英文:
Calling a function with unbounded generics with L-Value and R-Value
问题
我不完全理解为什么以下代码无法正常工作:
package org.example;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public interface MyInt<E> {
}
public static final class MyNum<E> implements MyInt<E> {
private E privados;
}
public static void f(List<? extends MyInt> l) {
System.out.println("from List<?>");
}
public static void main(String[] args) {
var m = new MyNum();
List<MyNum> l = Arrays.asList(m);
ArrayList<MyInt> r = l.stream().collect(Collectors.toCollection(ArrayList<MyInt>::new));
f(r);
f(l.stream().collect(Collectors.toCollection(ArrayList<MyInt>::new)));
}
}
对于 f(r)
传递的 l-值,它能够正常工作,但在下一行对 r-值版本的调用则无法。
很明显,编译器中发生的约束求解过程在程序员强制使用特定的 ArrayList<MyInt>
时是顺利的,但当我们要求它通过直接将 collect
的结果传递给 f
来进行约束求解时,编译器会发送类似以下错误的错误信息:
java: incompatible types: inferred type does not conform to upper bound(s)
inferred: java.lang.Object&java.util.List<? extends org.example.Main.MyInt>&java.util.Collection<org.example.Main.MyNum>
upper bound(s): java.util.Collection<org.example.Main.MyNum>,java.util.List<? extends org.example.Main.MyInt>,java.lang.Object
从错误消息中推断与上界之间唯一的区别:
- 使用了
&
而不是,
- 对象的顺序(颠倒)
我假设顺序不重要(它们是集合),而 &
和 ,
有特殊的含义。
有没有人能提供一个更详细的、不那么“手摆手”的解释发生了什么?
英文:
I don't fully understand why the following code, doesn't work:
package org.example;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public interface MyInt<E> {
}
public static final class MyNum<E> implements MyInt<E> {
private E privados;
}
public static void f(List<? extends MyInt> l) {
System.out.println("from List<?>");
}
public static void main(String[] args) {
var m = new MyNum();
List<MyNum> l = Arrays.asList(m);
ArrayList<MyInt> r = l.stream().collect(Collectors.toCollection(ArrayList<MyInt>::new));
f(r);
f(l.stream().collect(Collectors.toCollection(ArrayList<MyInt>::new)));
}
}
The call f(r)
passing the l-value works, but the call to the r-value version in the next line doesn't.
It is clear that the constraint solving process happening in the compiler is happy when we have a specific ArrayList<MyInt>
enforced by the programmer, but when we ask it to do the constraint solving end-to-end by passing the result of collect
directly to f
, the compiler would send an error like this:
java: incompatible types: inferred type does not conform to upper bound(s)
inferred: java.lang.Object&java.util.List<? extends org.example.Main.MyInt>&java.util.Collection<org.example.Main.MyNum>
upper bound(s): java.util.Collection<org.example.Main.MyNum>,java.util.List<? extends org.example.Main.MyInt>,java.lang.Object
The only differences between inferred vs upper bound (from the error message):
- the use of
&
vs,
- The order of objects (reversed)
I am assuming that the order is unimportant (they're sets) and that the &
and ,
have special meaning.
Someone has a more thorough less "hand-wavy" explanation of what's happening?
答案1
得分: 1
这个推断不一致是由于 lambda 表达式引起的。您可以在 OpenJDK.com 上的此邮件 中找到解释(主题:JDK-8219318 (???): 推断的类型与上界不符合在收集到 HashMap 时)。
也许新的 Java 编译器(javac)已经解决了这个问题。
如果您添加显式类型,这个问题就可以解决。示例:
f((ArrayList<? extends MyInt>) l.stream().collect(Collectors.toCollection(ArrayList<MyInt>::new)));
f((ArrayList<MyInt>) l.stream().collect(Collectors.toCollection(ArrayList<MyInt>::new)));
f((List<? extends MyInt>) l.stream().collect(Collectors.toCollection(ArrayList<MyInt>::new)));
f((List<MyInt>) l.stream().collect(Collectors.toCollection(ArrayList<MyInt>::new)));
此外,您还可以提供 Supplier 的实现。在这种情况下,您不需要添加显式类型。匿名类的示例:
f(l.stream().collect(Collectors.toCollection(new Supplier<List<MyInt>>(){
@Override
public List<MyInt> get() {
return new ArrayList<MyInt>();
}
})));
英文:
This inference incosistence is due to lambda expressions. You can get explanations in this mail on OpenJDK.com (subject: JDK-8219318 (???): inferred type does not conform to upper bound(s) when collecting to a HashMap).
Maybe new Java compilers (javac) have solved this problem.
That problem can be solved if you add explicit type. Examples:
f((ArrayList<? extends MyInt>) l.stream().collect(Collectors.toCollection(ArrayList<MyInt>::new)));
f((ArrayList<MyInt>) l.stream().collect(Collectors.toCollection(ArrayList<MyInt>::new)));
f((List<? extends MyInt>) l.stream().collect(Collectors.toCollection(ArrayList<MyInt>::new)));
f((List<MyInt>) l.stream().collect(Collectors.toCollection(ArrayList<MyInt>::new)));
Also, you can provide an implementation of Supplier. In this case, you don't need to add explicit type. Example of anonymous class:
f(l.stream().collect(Collectors.toCollection(new Supplier<List<MyInt>>(){
@Override
public List<MyInt> get() {
return new ArrayList<MyInt>();
}
})));
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论