demi
demi

Reputation: 5504

Java filter list to generic type T

How can I do the following (this doesn't compile):

<T> List<T> getElementsOf() {
    return list.stream()
            .filter(x -> x instanceof T)
            .map(x -> (T) x)
            .collect(toList());
}

What would be example of usage? Ideally, it should be like obj.<Derived>getElementsOf().

Upvotes: 10

Views: 8073

Answers (3)

Roland
Roland

Reputation: 23242

Note that generic type information is erased at runtime, so to make that even clearer I exchanged the T with Object. Actually the Object wouldn't be there too, but I kept it to make it clearer, where the T was:

List<Object> getElementsOf() {
    return list.stream()
        .filter(x -> x instanceof Object) // this is legal, as Object is an actual type, T isn't
        .map(x -> (Object) x)
        .collect(toList());
}

As you can see now, this doesn't make much sense. There is no way to infer the type without passing it as parameter. You already got the solution yourself and I only wanted to make it clearer also for others not so familiar with generics.

<T> List<T> getElementsOf(Class<T> type) {
    return list.stream()
               .filter(type::isInstance)
               .map(type::cast)
               .collect(toList());
}

If we exchange T now with Object, we will see, that the type is still here and so you can cast it appropriately:

List getElementsOf(Class type) {
    return list.stream()
               .filter(type::isInstance) // is type an Object? is it Long? we don't mind... we passed it and we deliver it
               .map(type::cast) // actually this isn't really needed, if you have a return type of List (as you already filtered for the types you want)
               .collect(toList());
}

Note also, that with that method you will never get null values when supplying a type. isInstance will return false in such a case.

Upvotes: 0

Sweeper
Sweeper

Reputation: 271050

Although the other answer pretty much does the job, here's a better one:

<T> List<T> getElementsOf(Class<T> clazz) {
    return list.stream()
            .filter(clazz::isInstance)
            .map(clazz::cast)
            .collect(toList());
}

Notice that the clazz::isInstance thingy. Instead of comparing the two classes, it uses the isInstance method. According to the docs, this is equivalent to instanceof, which is what you wanted in the first place.

This method is the dynamic equivalent of the Java language instanceof operator.

Upvotes: 25

demi
demi

Reputation: 5504

I got the following:

<T> List<T> getChildrenOf(Class<T> clazz) {
    return children.stream()
            .filter(node -> node.getClass() == clazz)
            .map(node -> clazz.<T>cast(node))
            .collect(toList());
}

List<Mesh> nameNodes = b.getChildrenOf(Mesh.class);

Upvotes: 1

Related Questions