Why can't I use Stream#toList to collect a list of a class' interface in Java 16?

17

I'm streaming objects of a class implementing an interface. I'd like to collect them as a list of elements of the interface, rather than the implementing class.

This seems impossible with Java 16.0.1's Stream#toList method. For example in the code below, the last statement will fail to compile.

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

public class WhyDodo {

    private interface Dodo { }

    private static class FancyDodo implements Dodo { }

    public static void main(String[] args) {
        final List<Dodo> dodos = Stream.of(new FancyDodo()).collect(Collectors.toList());
        final List<FancyDodo> fancyDodos = Stream.of(new FancyDodo()).toList();

        final List<Dodo> noFancyDodos = Stream.of(new FancyDodo()).toList();
    }
}

We could explicitly cast each element from FancyDodo to Dodo. But at least for brevity, we could just as well use .collect(Collectors.toList()).

Why can't I use Stream#toList to collect a list of a class' interface in Java 16?

And if anyone has a better solution than explicitly casting, I'd be happy to hear as well :)

Share
Improve this question
5
  • 9
    Try Stream.<Dodo>of(new FancyDodo()). With that factory method, you get Stream<FancyDodo>, which is not compatible with Stream<Dodo> – ernest_k May 13 at 10:01
  • Interesting question, as an end-user I would have expected it to behave more like the .collect(Collectors.toList()) unless the documentation could call that out. Another interesting line is final List<Dodo> fancyDodosAgain = Arrays.asList(Stream.of(new FancyDodo()).toArray(Dodo[]::new)) – Naman May 13 at 10:09
  • 2
    Because it returns a List<T>, where T is whatever it is a stream of. If you want a more abstract type, that's easy: List<Dodo> dodos = fancyDodos.stream().map(d -> (Dodo) d).toList() – Brian Goetz May 13 at 18:00
  • 2
    Alternately, you could say: List<? extends Dodo> = fancyDodos.stream().toList(), since a List<FancyDodo> is a List<? extends Dodo>. – Brian Goetz May 13 at 18:53
  • 1
    @Naman that’s just another case of the impossibility to declare that a method may return a broader type than another type parameter (hypothetical <U super T> List<U> toList()). This syntax does not exist because when Generics were introduced, the creators thought that this was rarely needed. It works with collect, because the toList collector introduces its own type variable which gets mapped to the Stream’s type parameter with a super bound at the collect call. Edit: oh, just saw this comment… – Holger 17 hours ago

Comments

Popular posts from this blog

Meaning of `{}` for return expression

Get current scroll position of ScrollView in React Native

flutter websocket connection issue