为什么类型推断在这里失败?

kaq*_*qao 8 java generics intellij-idea java-8

所以,我制作了这个相对简单的代码,并且我和IntelliJ IDEA都没有看到它有什么问题,但是javac在标记的行上徘徊,抱怨它无法推断出类型:

import java.util.List;
import java.util.stream.Collectors;

public class GenericsBreakJavac8 {

    public interface Edge<N> {
        N getNode();
    }

    @FunctionalInterface
    public interface EdgeCreator<N, E extends Edge<N>> {
        E createEdge(N node);
    }

    public static <N> List<Edge<N>> createEdges(List<N> nodes) {
        return createEdges(nodes, DefaultEdge::new); //the deadly line
    }

    //THE NEWLY ADDED LINE (see the edit note)
    public static <N> List<Edge<N>> createEdges2(List<N> nodes) {
        return createEdges(nodes, n -> new DefaultEdge<N>(n));
    }

    public static <N, E extends Edge<N>> List<E> createEdges(List<N> nodes, EdgeCreator<N, E> edgeCreator) {
        return nodes.stream().map(edgeCreator::createEdge).collect(Collectors.toList());
    }

    public static class DefaultEdge<N> implements Edge<N> {
        private final N node;

        public DefaultEdge(N node) {
            this.node = node;
        }

        @Override
        public N getNode() {
            return node;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用显式类型将有问题的行拆分为2会有所帮助,但类型签名比lambda长,完全违背了编写lambda的目的......

编辑: 如果我使用实际的lambda而不是方法参考,我再次遇到问题.请参阅上面新添加的方法.

Jef*_*rey 8

javac不喜欢那DefaultEdge是一种原始类型.

    return createEdges(nodes, DefaultEdge<N>::new);
Run Code Online (Sandbox Code Playgroud)

将按预期工作.

  • 是的,规范是以这种方式编写的,但这是一个糟糕的决定.将其称为原始类型会产生与"真实原始类型"的混淆,甚至早期的Java 8编译器也会在方法引用中生成原始类型警告.即使规范使用该措辞,我也会避免这种情况.将"DefaultEdge :: new"称为隐式类型的方法引用,将"DefaultEdge <N> :: new"称为显式类型的方法,使其更加清晰.顺便说一句,您还可以使用显式类型的lambda表达式来解决问题:`return createEdges(nodes,(N n) - > new DefaultEdge <>(n))`. (5认同)
  • @kaqqao`javac`在类型推断期间不考虑lambda体.它没有足够的信息来完全找出"EdgeCreator"的类型签名.您可以使用方法引用,也可以将类型参数完全指定为`createEdges`:`GenericsBreakJava8.<N,Edge <N >> createEdges(nodes,node - > new DefaultEdge <>(node))` (2认同)
  • 这实际上不被认为是原始类型:*"为方便起见,当泛型类型的名称用于引用实例方法(接收器成为第一个参数)时,目标类型用于确定类型参数.这样便于使用`Pair :: first`代替`Pair <String,Integer> :: first`.类似地,像`Pair :: new`这样的方法引用被视为"diamond"实例创建(`new pair <>()`).因为"菱形"是隐式的,所以这种形式不会实例化原始类型;实际上,没有办法表达对原始类型的构造函数的引用."* (2认同)
  • 该引用的来源是https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.13.1,在未标记的示例下. (2认同)