124

I have the following expression:

 scheduleIntervalContainers.stream() .filter(sic -> ((ScheduleIntervalContainer) sic).getStartTime() != ((ScheduleIntervalContainer)sic).getEndTime()) .collect(Collectors.toList()); 

...where scheduleIntervalContainers has element type ScheduleContainer:

final List<ScheduleContainer> scheduleIntervalContainers 

Is it possible to check the type before the filter?

0

6 Answers 6

211

You can apply another filter in order to keep only the ScheduleIntervalContainer instances, and adding a map will save you the later casts:

 scheduleIntervalContainers.stream() .filter(sc -> sc instanceof ScheduleIntervalContainer) .map(sc -> (ScheduleIntervalContainer) sc) .filter(sic -> sic.getStartTime() != sic.getEndTime()) .collect(Collectors.toList()); 

Or, as Holger commented, you can replace the lambda expressions with method references if you prefer that style:

 scheduleIntervalContainers.stream() .filter(ScheduleIntervalContainer.class::isInstance) .map(ScheduleIntervalContainer.class::cast) .filter(sic -> sic.getStartTime() != sic.getEndTime()) .collect(Collectors.toList()); 
Sign up to request clarification or add additional context in comments.

3 Comments

Or .filter(ScheduleIntervalContainer.class::isInstance) .map(ScheduleIntervalContainer.class::cast), whatever style you prefer.
In IDEA and Java8, if the above snippet is assigned to List<ScheduleContainer> scheduleIntervalContainers, it still prompts me to cast the result to List<ScheduleContainer> scheduleIntervalContainers explicitly, do you know why?
@K.Symbol Did you try to assign to a List<ScheduleContainer> or a List<ScheduleIntervalContainer>? It should be the latter.
141

A pretty elegant option is to use method reference of class:

scheduleIntervalContainers .stream() .filter( ScheduleIntervalContainer.class::isInstance ) .map( ScheduleIntervalContainer.class::cast ) .filter( sic -> sic.getStartTime() != sic.getEndTime()) .collect(Collectors.toList() ); 

5 Comments

What benefit is there to this style compared to using instanceof and (ScheduleIntervalContainer) to cast?
@MageWind that’s mostly a matter of style. Some people prefer it, because you don’t have to introduce another variable name (for the lambda parameter), others, because it generates slightly less byte code (not enough difference to be really relevant though).
This is really cool! But why is the .class required? isn't isInstance part of Object? Is Class a class in Java?
@PostSelf Indeed, it is and ScheduleIntervalContainer wouldn't really be a an instance.
Using method references instead of lambdas is usually more readable ;)
16

There is a small problem with @Eran solution - typing class name in both filter and map is error-prone - it is easy to forget to change the name of the class in both places. An improved solution would be something like this:

private static <T, R> Function<T, Stream<R>> select(Class<R> clazz) { return e -> clazz.isInstance(e) ? Stream.of(clazz.cast(e)) : null; } scheduleIntervalContainers .stream() .flatMap(select(ScheduleIntervalContainer.class)) .filter( sic -> sic.getStartTime() != sic.getEndTime()) .collect(Collectors.toList()); 

However there might be a performance penalty in creating a Stream for every matching element. Be careful to use it on huge data sets. I've learned this solution from @Tagir Vailev

3 Comments

In this approach you must take care of NullPointerExceptions, since select(A.class) will return null for anything that is not an A. Adding .filter(Objects::nonNull) would help. BTW: @Eran's approach is null-safe.
Sorry, my bad... JavaDoc of flatMap says "If a mapped stream is null an empty stream is used, instead.". So your solution was correct, even without the null-filter.
Still (IMO) odd to return null when you could've just returned an empty stream
3

Instead of a filter + map like other answers suggest, I would recommend this utility method:

public static <Super, Sub extends Super> Function<Super, Stream<Sub>> filterType(Class<Sub> clz) { return obj -> clz.isInstance(obj) ? Stream.of(clz.cast(obj)) : Stream.empty(); } 

Use it as:

Stream.of(dog, cat fish) .flatMap(filterType(Dog.class)); 

Compared to filter + map it has the following advantages:

  • If the class does not extend your class you will get a compile error
  • Single place, you can never forget to change a class in either filter or map

Comments

2

Filter by class type with StreamEx

StreamEx.of(myCollection).select(TheThing.class).toList(); 

Comments

0

Not exactly a stream, but Guava's FluentIterable does this very cleanly:

FluentIterable.from(scheduleIntervalContainers) .filter(ScheduleIntervalContainer.class) .filter(sic -> sic.getStartTime() != sic.getEndTime()) .toList(); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.