Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't this Java 8 stream example compile?

I'm trying to figure out why this code does not compile on JDK 1.8.0_45:

public class Example<E extends Example<E>> {
    public List<? extends Example<?>> toExamples(Collection<String> collection) {
        return collection.stream()
                .map(v -> lookup(v))
                .collect(Collectors.toList());
    }

    public static <E extends Example<E>> E lookup(String value) {
        return null;
    }
}

Adding a seemingly unnecessary cast fixes it:

public class Example<E extends Example<E>> {
    public List<? extends Example<?>> toExamples(Collection<String> collection) {
        return collection.stream()
                .map(v -> (Example<?>) lookup(v))
                .collect(Collectors.toList());
    }

    public static <E extends Example<E>> E lookup(String value) {
        return null;
    }
}

Here's the error from the compiler:

Example.java:9: error: incompatible types: inference variable R has incompatible bounds
              .collect(Collectors.toList());
                      ^
  equality constraints: List<Object>
  upper bounds: List<? extends Example<?>>,Object
where R,A,T are type-variables:
  R extends Object declared in method <R,A>collect(Collector<? super T,A,R>)
  A extends Object declared in method <R,A>collect(Collector<? super T,A,R>)
  T extends Object declared in interface Stream

For some reason, the return type of lookup() isn't correctly inferred to something extending Example.

like image 833
Stijn Van Bael Avatar asked Jun 02 '15 14:06

Stijn Van Bael


People also ask

Does Java 8 support streams?

Java 8 offers the possibility to create streams out of three primitive types: int, long and double. As Stream<T> is a generic interface, and there is no way to use primitives as a type parameter with generics, three new special interfaces were created: IntStream, LongStream, DoubleStream.

What is stream in Java 8 example?

Introduced in Java 8, the Stream API is used to process collections of objects. A stream is a sequence of objects that supports various methods which can be pipelined to produce the desired result. A stream is not a data structure instead it takes input from the Collections, Arrays or I/O channels.

What are two types of streams in Java 8?

With Java 8, Collection interface has two methods to generate a Stream. stream() − Returns a sequential stream considering collection as its source. parallelStream() − Returns a parallel Stream considering collection as its source.


2 Answers

As Peter Lawrey pointed out, ? extends Example<?> is not compatible with E extends Example<E>. Still, even fixing the signature doesn’t make type inference work here.

The reason is a known limitation of the type inference as it does not back-propagate through chained method invocations. In other words, the return type allows to infer the types for the collect(…) invocation but not for the preceding map(…) invocation. (see also this answer)

But it works for nested method invocations, so the following rewritten method can be compiled:

public class Example<E extends Example<E>> {
    public <E extends Example<E>> List<E> toExamples(Collection<String> collection) {
        return collection.stream()
            .collect(Collectors.mapping(v -> lookup(v), Collectors.toList()));
    }

    public static <E extends Example<E>> E lookup(String value) {
        return null;
    }
}

Still, you have to rethink the semantics of your code. A method’s type parameter which appears only at the return type can’t be correct as it implies that “whatever the caller substitutes for this type parameter, the method will return the right thing”. Since the method implementation doesn’t know what the caller assumes, this is impossible. Only returning null or an empty list will work correctly, which is of little use.

like image 78
Holger Avatar answered Oct 28 '22 07:10

Holger


When you have a ? it doesn't equal another ? i.e. the compiler doesn't see

? extends Example<?>

as a match for

E extends Example<E>

as it cannot assume the two ? are the same. It could be

A extends Example<B>

When you perform the cast, you obscure the constraint so it can match.

like image 28
Peter Lawrey Avatar answered Oct 28 '22 06:10

Peter Lawrey