Amirak
Amirak

Reputation: 63

Replace guava Fluent Iterables with Streams

I have a method for a filter by type, it gets as input Collection<?> collection and Class<T> tClass. I want to replace FluentIterable by the Stream.

public static <T> List<T> filterByType(final Collection<?> filerCollection, final Class<T> classType) 
{
    return FluentIterable.from(filerCollection).filter(classType).toList();
}

I tried with this solution:

public static <T> List<T> typeFilter(final Collection<?> filter, final Class<T> classType) {
    return (List<T>) filter.stream()
            .filter(Objects::nonNull)
            .map(Object.class::cast)
            .collect(Collectors.toList());
}

how to do that without casting the return Stream?

Upvotes: 2

Views: 1490

Answers (4)

ColinD
ColinD

Reputation: 110036

Another thing you can do (though it's probably a bit less efficient than doing it in two steps) is to define something like this:

public static <T> Function<Object, Stream<T>> instancesOf(Class<T> c) {
  return o -> c.isInstance(o) ? Stream.of(c.cast(o)) : Stream.empty();
}

and then use it like this:

return filter.stream()
    .flatMap(instancesOf(classType))
    .collect(toList());

Upvotes: 0

Lino
Lino

Reputation: 19926

You forgot to check if your elements are even of type T. I.e. you have to filter those elements out first:

return filter.stream()
             .filter(classType::isInstance) // only keep elements of type T
             .map(classType::cast)          // safely cast from Object to T
             .collect(Collectors.toList()); // collect into a List<T>

Class#isInstance() also directly takes care of null values, so you don't have to use filter(Object::nonNull).

Upvotes: 4

Nikolas
Nikolas

Reputation: 44368

Look carefully what FluentIterable::filter(Class<T>) does and compare it with maps each item to the classType with casting. Your intention is to filter those items of classType.

Next, the classType::cast should be used instead of Object.class::cast since the desired class is already passed through Class<T> classType and might be used directly as a method reference. The Object.class::cast casts to a parent Object which every object inherits from.

Here is what you want:

public static <T> List<T> typeFilter(final Collection<?> filter, final Class<T> classType) {
    return filter.stream()
        .filter(Objects::nonNull)
        .filter(item -> item.getClass().equals(classType))
        .map(classType::cast)
        .collect(Collectors.toList());
}

Edit: As mentioned in another answer, the filter(classType::isInstance) might be used as well.

Upvotes: 1

Yassin Hajaj
Yassin Hajaj

Reputation: 21965

You should use classType::cast because here you're using Object.class::cast which basically does nothing

public static <T> List<T> typeFilter(final Collection<?> filter, final Class<T> classType) {
    return filter.stream()
                 .filter(Objects::nonNull)
                 .map(classType::cast)
                 .collect(Collectors.toList());
}

And to be even better, add a filter(classType::isInstance) to your method chaining

Upvotes: 1

Related Questions