Please add Stream.filterByType(Class) and possibly Stream.forEachOfType(Class, Consumer)

Derek Foster vapor1 at teleport.com
Wed May 1 12:38:39 PDT 2013


Hi, folks!

I've been modifying some of my old projects to use one of the recent Lambda-enabled JDKs. One issue that I have run across is code that handles heterogenous collections. Typically, I want to iterate over the collection, doing something with only the items within the collection that are subtypes of a specific type. In Java 7, this usually requires both an instanceof test and a cast, like this:

class Thing extends Item { ... }

List<Thing> getThings(List<Item> items) {
  List<Thing> things = new ArrayList<>();
  for (Item item : items) {
    if (item instanceof Thing) {
      things.add((Thing)item);
    }
  }
  return things;
}

With the current implementation of streams, I currently have to rewrite this as something like:

List<Thing> getThings(List<Item> items) {
   return items.stream()
               .filter(item -> item instanceof Thing)
               .map(item -> (Thing)item)
               .collect(Collections.toList());
}

This works, but it seems inelegant to have to 'stutter' by repeating the type that I am converting to twice (in an instanceof and in a cast). It also introduces the possibility for error if someone modifies one of these without modifying the other. Since the instanceof and the cast are so tightly linked, I would prefer to apply the "Don't Repeat Yourself" principle, and write this as:

List<Thing> getThings(List<Item> items) {
  return items.stream()
              .filterByType(Thing.class)
              .collect(Collections.toList()).
}

This could be easily implemented in the JDK by adding a method to Stream such as:

default <S> Stream<S> filterByType(Class<S> type) {
    return filter(item -> type.isInstance(item))
             .map(item -> type.cast(item));
}

although better-performing implementations (that don't create an intermediate Stream object) are certainly possible.

I think that having such a method in Stream would often be useful. For instance, this kind of heterogenous collection shows up quite a bit in various "list of listeners" implementations, where a method performing a listener callback must iterate a list of listeners, looking for a listener of a type which can handle the callback in question, and then invoke the callback on that listener:

void fireNiftyEvent(NiftyEvent event) {
    this.listeners.stream()
                  .filterByType(NiftyEventListener.class)
                  .forEach(listener -> listener.handleNiftyEvent(event));
}

Internally, a 'filterByType' followed by a 'forEach' could be optimized (to a forEachOfType(Class, Consumer)) to make the iteration more efficient. It might even be desirable to have a forEachOfType method exposed as a public method on Stream, to make the above example even shorter:

void fireNiftyEvent(NiftyEvent event) {
    this.listeners.stream().forEachOfType(NiftyEventListener.class, listener -> listener.handleNiftyEvent(event));
}

Anyway, what would I need to do to encourage the addition of either or both of these methods to the Stream class?



More information about the lambda-libs-spec-observers mailing list