使用具有无界泛型的函数,并且具有左值和右值

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

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

从错误消息中推断与上界之间唯一的区别:

  1. 使用了 & 而不是 ,
  2. 对象的顺序(颠倒)

我假设顺序不重要(它们是集合),而 &, 有特殊的含义。

有没有人能提供一个更详细的、不那么“手摆手”的解释发生了什么?

英文:

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):

  1. the use of & vs ,
  2. 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>();
  }
})));

huangapple
  • 本文由 发表于 2023年7月6日 20:36:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76628913.html
匿名

发表评论

匿名网友

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

确定