I have a set of domain objects that inherit from a shared type (i.e. GroupRecord extends Record
, RequestRecord extends Record
). The subtypes have speci
It depends on what do you find "more concise and readable". I myself would argue that the way you already implemented is fine as it is.
However, there is indeed a way to do this in a way that is slightly shorter from the point of where you use it, by using Stream.flatMap
:
static Function> onlyTypes(Class cls) {
return el -> cls.isInstance(el) ? Stream.of((T) el) : Stream.empty();
}
What it would do is it will convert each original stream element to either a Stream
of one element if the element has expected type, or to an empty Stream
if it does not.
And the use is:
records.stream()
.flatMap(onlyTypes(GroupRecord.class))
.forEach(...);
There are obvious tradeoffs in this approach:
onlyTypes
is needed.Stream
objects are relatively heavyweight, and creating so much of them may result in performance degradation. But you should not trust my word here and profile both variants under heavy load.Edit:
Since the question asks about reusing filter
and map
in slightly more general terms, I feel like this answer can also discuss a little more abstraction. So, to reuse filter and map in general terms, you need the following:
static Function> filterAndMap(Predicate super E> filter, Function super E, R> mapper) {
return e -> filter.test(e) ? Stream.of(mapper.apply(e)) : Stream.empty();
}
And original onlyTypes
implementation now becomes:
static Function> onlyTypes(Class cls) {
return filterAndMap(cls::isInstance, cls::cast);
}
But then, there is yet again a tradeoff: resulting flat mapper function will now hold captured two objects (predicate and mapper) instead of single Class
object in above implementation. It may also be a case of over-abstracting, but that one depends on where and why you would need that code.