Why does type inference not work?

So, I made this relatively simple code, and neither I nor IntelliJ IDEA see anything bad in it, but javac keel on the marked line, complaining that it cannot infer types:

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; } } } 

Dividing the problematic string into 2 with explicit types helps, but the type signature is longer than lambda, completely defeating the purpose of writing lambda in the first place ...

EDIT: If I use the actual lambda instead of the method reference, I get this problem again. See Recently Added Method.

+8
java generics intellij-idea java-8
source share
2 answers

javac doesn't like that DefaultEdge is a raw type.

  return createEdges(nodes, DefaultEdge<N>::new); 

will work as expected.

+7
source share

In addition to Jeffreys Response , note that your EdgeCreator interface EdgeCreator no different from Function in its functionality, and createEdges really does not need problematic types of constraints.Thus, one of the solutions is to have EdgeCreator extend Function to simplify createEdges .

 import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; public class GenericsBreakJavac8 { public interface Edge<N> { N getNode(); } @FunctionalInterface public interface EdgeCreator<N, E extends Edge<N>> extends Function<N,E> { E createEdge(N node); @Override public default E apply(N t) { return createEdge(t); } } public static <N> List<Edge<N>> createEdges(List<N> nodes) { return createEdges(nodes, DefaultEdge::new); } public static <N> List<Edge<N>> createEdges2(List<N> nodes) { return createEdges(nodes, n -> new DefaultEdge<>(n)); } public static <T,R> List<R> createEdges(List<T> nodes, Function<T,R> edgeCreator) { return nodes.stream().map(edgeCreator).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; } } } 

You can also remove the EdgeCreator interface and use Function<N, E extends Edge<N>> in places where forced enforcement is required, but there is no such place in this code. The restriction will continue when someone tries to use createEdges(List,Function) , respectively. its result in a context where a List<Edge<SpecificNodeType>> is required.

Failure to comply with type restrictions over too many code spaces will simply inflate your source code without any benefits.

By the way, in your current code you don’t even need the DefaultEdge class; you can simplify all code to

 import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; public class GenericsBreakJavac8 { public interface Edge<N> { N getNode(); } public static <N> List<Edge<N>> createEdges(List<N> nodes) { return createEdges(nodes, n -> () -> n); } public static <T,R> List<R> createEdges(List<T> nodes, Function<T,R> edgeCreator) { return nodes.stream().map(edgeCreator).collect(Collectors.toList()); } } 
+4
source share

All Articles