Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 :)

like image 978
TTT Avatar asked May 13 '21 09:05

TTT


People also ask

Why is stream not working?

Make sure your browser is up-to-date, too - head to the settings menu and check for any updates, then install them and restart your browser. Browser extensions can create slowdown on web pages, including for video streaming services.

What to do if stream is not opening?

There may be tiny problems with the programs or processes on your operating system that stop your Steam client from opening. Or maybe the state or the cache of your computer are interfering with your client. You can try restarting your computer to get rid of these issues. Then check to see if you can open Steam.

Why can I not live stream?

Your channel's live streaming ability will be automatically turned off or suspended for any of the following reasons: Your channel got a Community Guidelines strike. Your live stream or archived live stream is blocked globally. Your live stream or archived live stream gets a copyright takedown.

Why isn’t media streaming working?

Media Streaming is linked to Windows Search Indexing and can be affected by it. If you have disabled Windows Search Indexing for some reason, you have to re-enable it before Media Streaming will start working. Follow these easy steps to enable Windows Search Indexing:

Why can't I access Microsoft Stream?

Solution Re: Can't access Microsoft Stream Hi, usually the best way to get specific technical support is opening a service request, so the particulars can be investigated, this can be done online or via phone, this is aimed at administrators typically who manage Office 365 for their organisation:

What is Microsoft Stream and how does it work?

Microsoft Stream supports Microsoft Edge and the current versions of Chrome and Safari. What is an encoder? Simply put, an encoder compresses audio and video from various inputs and sends that output to a streaming service, such as Microsoft Stream. Typically, there are hardware, software and mobile apps that you can use.

Why can't Windows Media Player play a video stream?

CANT PLAY VIDEO STREAM IT KEEPS TELLING ME Windows Media Player cannot play the file. The Player might not support the file type or might not support the codec that was used to compress the file. This thread is locked. You can follow the question or vote as helpful, but you cannot reply to this thread.


3 Answers

.collect(Collectors.toList()) works because the signature of collect is:

<R, A> R collect(Collector<? super T, A, R> collector);

the important part being ? super T

which means the toList() collector can be interpreted as Collector<Dodo,?,List<Dodo> (when you assign the result of .collect() to a List<Dodo>) even though the type of your stream is Stream<FancyDodo>.

On the other hand, the signature of Stream's toList() is:

List<T> toList()

so if you execute it for a Stream<FancyDodo>, you'll get a List<FancyDodo>, which can't be assigned to a List<Dodo> variable.

I suggest you simply use stream.collect(Collectors.toList()) instead of stream.toList() in this case.

like image 178
Eran Avatar answered Oct 20 '22 21:10

Eran


Because Stream.toList is declared to return a List<T>:

default List<T> toList() { ... }

where T is the element type of the stream. I can't really think of an alternative way of declaring toList so that it can return your desired type of list. The best you can do is to accept a List<? super T> as argument, and add the stream elements to it, but that kind of goes against the "aesthetics" of streams - the whole point of this is to be declarative and have little state.

One way you can rewrite your code to make toList return a list of your desired type, is to specify the type of T manually. Right now T is inferred to be FancyDodo due to Stream.of(new FancyDodo()), but you can force T to be Dodo if you want:

Stream.<Dodo>of(new FancyDodo()).toList();

Now T is Dodo, toList will return a List<Dodo>.


The best you can do is to accept a List<? super T> as argument, and add the stream elements to it

Actually, this is kind of what Collector is doing. Notice how collect accepts a Collector<? super T, DoesntMatter, R>, and returns R. That contravariant ? super T is what enables you to use a toList collector like that. Note also that R is a generic parameter of collect, which means that you get to decide what collect returns, as long as you can provide a collector that collects ? super T to R.

like image 33
Sweeper Avatar answered Oct 20 '22 20:10

Sweeper


Generic type argument resolution happens one method call at a time.

Stream.of(new FancyDodo()) will always resolved T to FancyDodo, so will always result in a Stream<FancyDodo>.

toList() doesn't resolve T, it just uses the already-established T, so the result is always List<FancyDodo>, and List<FancyDodo> is not compatible with List<Dodo>. See: "Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic?"

collect(Collectors.toList()) has a different T in the Collectors.toList(), that can resolve differently from the T of the Stream. The compiler resolves that T as Dodo, because of the desired return type of List<Dodo>.

like image 36
Andreas Avatar answered Oct 20 '22 22:10

Andreas