2

Is there way to collect() generic items from a stream ?

This is what I want to do...

private <T extends Data> List<Response<T>> validateAndGetResponses(List<Response> responses, Class<T> clazz) {
        Supplier<List<Response<T>>> supplier = LinkedList::new;

        List<Response<T>> list = responses.stream().filter(
                response -> clazz.isInstance(getData(response))).collect(Collectors.toCollection(supplier));
        return list;

}

This doesn't work, I get

no suitable method found for collect(....)

The Coder
  • 3,447
  • 7
  • 46
  • 81
Nati
  • 1,034
  • 5
  • 19
  • 46
  • Please post the full error message. Even if you don't understand it, it will help us help you. – Jeffrey Bosboom Mar 12 '16 at 23:59
  • This compiles just fine. Could you post your imports? – Tunaki Mar 13 '16 at 00:06
  • 2
    I agree with @JeffreyBosboom. So far the code you wrote even compile fine. The only thing off is responses argument that should probably be List>. – fabriziocucci Mar 13 '16 at 00:07
  • @fabriziocucci - that indeed was the issue. Thanks ! – Nati Mar 13 '16 at 00:08
  • And just as a side note: sometimes (not here obviously), a method returning something generic needs a type hint, meaning that you have to do `foo. someMethod(.... ` instead of `foo.someMethod(.... ` – GhostCat Mar 13 '16 at 00:17

2 Answers2

5

So, if the purpose of the code is indeed filtering the Response objects based on the type parameter, a wild guess of the solution could be:

@SuppressWarnings("unchecked")
private <T extends Data> List<Response<T>> validateAndGetResponses(List<Response<? extends Data>> responses, Class<T> clazz) {
    return responses.stream()
            .filter(response -> clazz.isInstance(getData(response)))
            .map(response -> (Response<T>) response)
            .collect(Collectors.toCollection(LinkedList::new));
}
fabriziocucci
  • 782
  • 8
  • 20
  • This makes me really wary. What if the `List` they are passing in is a `List`? You've just inadvertently caused heap pollution. They appear to be filtering based on a type check so this seems likely. OP needs to go and remove *all* [raw types](http://stackoverflow.com/q/2770321/2891664) from their code. – Radiodef Mar 13 '16 at 00:18
  • My mistake, I'm sorry. what @Radiodef wrote is true. since i'm filtering based on type, not all my list is `Response`. it's more of `List>` which brings me back to my original question... – Nati Mar 13 '16 at 00:24
  • It's possible that what you need is just like a `map(r -> (Response) r)` in your stream but what you're doing is too vague to me to suggest a solution without knowing more about it. What will compile is not necessarily what is correct & provably safe here, especially if `Response` is mutable. Unless you just don't care about unsafe generic code, I guess, but I've seen code like this blow up too many times on here. – Radiodef Mar 13 '16 at 00:31
  • @Radiodef Response is immutable, in this case. so filtering by class and then immediately casting to the right type is my best option ? – Nati Mar 13 '16 at 00:44
  • 1
    @Radiodef made a good point indeed. I'm guessing too what you code is meant to do and just making the compiler happy could do more harm than good! :) But yes, if you are just filtering the responses based on the class literal you are passing then the casting is probably your only option. – fabriziocucci Mar 13 '16 at 00:49
  • Semantically it makes more sense to me to cast after you've done the type filter. – Radiodef Mar 13 '16 at 00:58
  • @Radiodef please look at my answer - if think it's the best option we can get. – Nati Mar 13 '16 at 08:43
5

So the problem was I used a raw type List<Response> responses as an argument, though I really should of used a wildcard boundary, List<Response<? extends Data>> responses.

This is the complete method:

@SuppressWarnings("unchecked")
private  <T extends Data> List<T> validateAndGetResponses(List<Response<? extends Data>> responses, Class<T> clazz) {
    return responses.stream().map(this::getData)
                             .filter(clazz::isInstance)
                             .map(r -> (T) r)
                             .collect(Collectors.toList());
}
Nati
  • 1,034
  • 5
  • 19
  • 46
  • 1
    Ok so now you are saying that you just want to return a List instead of a List>. This is the only difference with my response and should be fine. – fabriziocucci Mar 13 '16 at 08:54