HiveBrain v1.2.0
Get Started
← Back to all entries
snippetjavaMinor

Filter then map instances from a stream and other Java 8 hiccups

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
streammapinstanceshiccupsotherjavafilterthenandfrom

Problem

I'm having a hard time rewriting this method to something more legible and understandable for the reader.

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 of T;



  • MyTypeWrapper is defined as final class MyTypeWrapper;



  • MyType is defined as abstract 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 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.