3

I was getting type mismatch errors, until I refactored code to this:

    public final Stream<Map.Entry<E, Integer>> orderedStreamOfEntries() {
        return this.m_map.entrySet()
                .stream()
                .sorted(Comparator.comparingInt(Entry::getValue))
                .map(AbstractMap.SimpleImmutableEntry::new);            
    }
  • The return type is Stream<Entry<E, Integer>>
  • The type of the stream at the end of this routine is Stream<SimpleImmutableEntry<E, Integer>>

The formal type parameter E has this definition:

<E extends Enum<E> & MyCustomInterface>

I don't understand why this appears to be acceptable to the compiler. Because Java generics are invariant, even though java.util.AbstractMap.SimpleImmutableEntry implements java.util.Map.Entry, I would have said that Stream<SimpleImmutableEntry<>> is not a subtype of the return type, Stream<Entry<>>.

scottb
  • 9,908
  • 3
  • 40
  • 56
  • what is the definition of `E` from the class definition? – Sled Jul 27 '17 at 17:15
  • @ArtB: I've edited the definition into the question – scottb Jul 27 '17 at 17:18
  • When in doubt when debugging I recommend breaking each operation on the stream as an assignment to a specific variable. Using an IDE with refactoring support makes this trivial. That'll let you see the types evolve. – Sled Jul 27 '17 at 17:41

1 Answers1

3

You're making two mistakes. The first is assuming SimpleImmutableEntry::new is a Function<Entry, SimpleImmutableEntry>*, when in fact it can be interpreted as a Function<Entry, Entry> which happens to return a SimpleImmutableEntry.

Secondly, look at the signature for map():

<R> Stream<R> map(Function<? super T, ? extends R> mapper)

By returning ? extends R, the method is free to interpret R as a superclass of the lambdas return type, meaning even a Function<Entry, SimpleImmutableEntry> could result in a Stream<Entry>, or even Stream<Object>.

The actual interpretation depends on the inferred return type, which in your case is Stream<Entry>.

*Raw entry types used for brevity.

shmosel
  • 49,289
  • 6
  • 73
  • 138
  • <<*, when in fact it can be interpreted as a Function which happens to return SimpleImmutableEntry.>>> That seems to be the key, – Sled Jul 27 '17 at 17:39
  • @scottb Yes, it's the same thing. The point is that a `Function` may (read: must) return a subclass of `Entry`. – shmosel Jul 27 '17 at 17:40
  • Yes, I understand what you are saying. What confuses me is that my IDE shows the return type from the `map()` function as `Stream>`. if this is truly the return type, then it seems that there ought to be an error. – scottb Jul 27 '17 at 17:43
  • @scottb Probably just a bug in your IDE. Type inference is notoriously difficult to get right. – shmosel Jul 27 '17 at 17:45
  • @scottb That's curious because [Java 6's compiler API](http://javabeat.net/the-java-6-0-compiler-api/) was added so that IDEs use the same code as the javac compiler itself. I believe all IDEs but Eclipse made the switch (which IIRC was because they needed to support continuous compilation in the background). – Sled Jul 27 '17 at 17:56
  • @shmosel: I agree that the compiler is most likely inferring `Map.Entry<>` as the return type from `? extends R`. This is a sensible explanation. Thanks for taking the time to explain. – scottb Jul 27 '17 at 17:56
  • @scottb which IDE are you using btw? – Sled Jul 27 '17 at 18:00
  • @ArtB: This is NetBeans v. 8.2 that I'm reading the type information from. – scottb Jul 27 '17 at 21:19