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