snippetjavaMinor
Filter then map instances from a stream and other Java 8 hiccups
Viewed 0 times
streammapinstanceshiccupsotherjavafilterthenandfrom
Problem
I'm having a hard time rewriting this method to something more legible and understandable for the reader.
Here are the tools/classes I use:
What annoys me with this code is that it's barely legible, mostly because of the succession of filter on instance then map. I tried to find in Java 8 something similar to Guava's
Anyways, any input is welcome.
Given the example below, this method will keep/skip various fields.
Edit:
Originally the method was this:
```
List> types = ne
static Stream> collectMyTypes(Object o) {
return Reflection.lineage(o.getClass())
.flatMap(c -> Arrays.stream(c.getDeclaredFields()))
.map(Field::getGenericType)
.filter(t -> t instanceof ParameterizedType)
.map(t -> (ParameterizedType) t)
.filter(t -> t.getRawType() == MyTypeWrapper.class)
.map(t -> t.getActualTypeArguments()[0]) //MyTypeWrapper is final and has only 1 type argument.
.filter(t -> t instanceof Class)
.map(t -> (Class) t);
}Here are the tools/classes I use:
Reflection.lineage(Class)iterates over all parents classes ofT;
MyTypeWrapperis defined asfinal class MyTypeWrapper;
MyTypeis defined asabstract class MyType.
What annoys me with this code is that it's barely legible, mostly because of the succession of filter on instance then map. I tried to find in Java 8 something similar to Guava's
Iterables.filter(Iterable,Class) but haven't found any (yet, I implemented a similar method, but the code then looks like Lisp). Also, the call to Arrays.stream disturbs me. Yeah, I could split it into .map(Class::getDeclaredFields).flatten(Arrays::stream) but this would add some unnecessary complexity, wouldn't it?Anyways, any input is welcome.
Given the example below, this method will keep/skip various fields.
class GoodExample extends MyType {}
class BadExample extends MyType {}
class Example {
// GoodExample will be returned.
MyTypeWrapper object1;
// BadExample will be skipped because the type is a wildcard
MyTypeWrapper object2;
// There is no type to return so this field is skipped.
MyTypeWrapper object3;
// This is not an instance of MyTypeWrapper so it's skipped.
Object object4;
// This is generic, but still not a MyTypeWrapper, so it's skipped.
List object5;
}Edit:
Originally the method was this:
```
List> types = ne
Solution
What I can suggest is putting the subsequent
You can also put the
For convenience, you can create a
Then, the meat of
Finally, your
filter()/map() steps into an extraction method of sorts:@SuppressWarnings("rawtypes")
private static Optional extractMyType(Field field) {
return Optional.of(field.getGenericType())...;
}You can also put the
filter()/map() steps as a single method:private static Optional filterAndMap(T obj, Predicate filter,
Function mapper) {
return Optional.of(obj).filter(filter).map(mapper);
}For convenience, you can create a
cast() method that calls filterAndMap() to handle safe-casting:private static Optional cast(T obj, Class clazz) {
return filterAndMap(obj, clazz::isInstance, v -> (R) obj);
}Then, the meat of
extractMyType() effectively becomes three flatMap() operations:@SuppressWarnings("rawtypes")
private static Optional extractMyType(Field field) {
return Optional.of(field.getGenericType())
.flatMap(v -> cast(v, ParameterizedType.class))
.flatMap(v -> filterAndMap(v,
v1 -> v1.getRawType() == MyTypeWrapper.class,
v1 -> v1.getActualTypeArguments()[0]))
.flatMap(v -> cast(v, Class.class));
}Finally, your
collectMyTypes() method will just do a simple filter on the resulting Optional wrapper instances:@SuppressWarnings("unchecked")
static Stream> collectMyTypes(Object o) {
return Reflection.lineage(o.getClass())
.flatMap(c -> Arrays.stream(c.getDeclaredFields()))
.map(YourClass::extractMyType).filter(Optional::isPresent)
.map(Optional::get);
}Code Snippets
@SuppressWarnings("rawtypes")
private static Optional<Type> extractMyType(Field field) {
return Optional.of(field.getGenericType())...;
}private static <T, R> Optional<R> filterAndMap(T obj, Predicate<? super T> filter,
Function<? super T, ? extends R> mapper) {
return Optional.of(obj).filter(filter).map(mapper);
}private static <T, R> Optional<R> cast(T obj, Class<R> clazz) {
return filterAndMap(obj, clazz::isInstance, v -> (R) obj);
}@SuppressWarnings("rawtypes")
private static Optional<Class> extractMyType(Field field) {
return Optional.of(field.getGenericType())
.flatMap(v -> cast(v, ParameterizedType.class))
.flatMap(v -> filterAndMap(v,
v1 -> v1.getRawType() == MyTypeWrapper.class,
v1 -> v1.getActualTypeArguments()[0]))
.flatMap(v -> cast(v, Class.class));
}@SuppressWarnings("unchecked")
static Stream<Class<? extends MyType>> collectMyTypes(Object o) {
return Reflection.lineage(o.getClass())
.flatMap(c -> Arrays.stream(c.getDeclaredFields()))
.map(YourClass::extractMyType).filter(Optional::isPresent)
.map(Optional::get);
}Context
StackExchange Code Review Q#98952, answer score: 3
Revisions (0)
No revisions yet.