Filter then map instances from a stream and other Java 8 hiccups I'm having a hard time rewriting this method to something more legible and understandable for the reader.

static Stream&lt;Class&lt;? extends MyType&gt;&gt; collectMyTypes(Object o) {
  return Reflection.lineage(o.getClass())
      .flatMap(c -&gt; Arrays.stream(c.getDeclaredFields()))
      .map(Field::getGenericType)
      .filter(t -&gt; t instanceof ParameterizedType)
      .map(t -&gt; (ParameterizedType) t)
      .filter(t -&gt; t.getRawType() == MyTypeWrapper.class)
      .map(t -&gt; t.getActualTypeArguments()[0]) //MyTypeWrapper is final and has only 1 type argument.
      .filter(t -&gt; t instanceof Class)
      .map(t -&gt; (Class&lt;? extends MyType&gt;) t);
}


Here are the tools/classes I use:


Reflection.lineage(Class&lt;T&gt;) iterates over all parents classes of T;
MyTypeWrapper is defined as final class MyTypeWrapper&lt;T extends MyType&gt;;
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&lt;?&gt;,Class&lt;T&gt;) 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&lt;GoodExample&gt; object1; 

  // BadExample will be skipped because the type is a wildcard
  MyTypeWrapper&lt;? extends BadExample&gt; 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&lt;BadExample&gt; object5;
}


Edit:

Originally the method was this:

  List&lt;Class&lt;? extends MyType&gt;&gt; types = new ArrayList&lt;&gt;();
  Reflection.lineage(o.getClass())
      .forEach((c) -&gt; {
        for (Field f: c.getDeclaredFields()) {
          Type type = f.getGenericType();
          if (!(type instanceof ParameterizedType)) {
            continue;
          }
          ParameterizedType pType = (ParameterizedType)type;
          if (pType.getRawType() != MyTypeWrapper.class) {
            continue;
          }
          // length is 1.
          Type argType = pType.getActualTypeArguments()[0];
          if (!(argType instanceof Class)) {
            continue;
          }
          Class&lt;? extends MyType&gt; argClass = (Class&lt;? extends MyType&gt;)argType;
          types.add(argClass);
        }
      });
return types;
What I can suggest is putting the subsequent filter()/map() steps into an extraction method of sorts:

@SuppressWarnings("rawtypes")
private static Optional&lt;Type&gt; extractMyType(Field field) {
    return Optional.of(field.getGenericType())...;
}


You can also put the filter()/map() steps as a single method:

private static &lt;T, R&gt; Optional&lt;R&gt; filterAndMap(T obj, Predicate&lt;? super T&gt; filter,
        Function&lt;? super T, ? extends R&gt; 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 &lt;T, R&gt; Optional&lt;R&gt; cast(T obj, Class&lt;R&gt; clazz) {
    return filterAndMap(obj, clazz::isInstance, v -&gt; (R) obj);
}


Then, the meat of extractMyType() effectively becomes three flatMap() operations:

@SuppressWarnings("rawtypes")
private static Optional&lt;Class&gt; extractMyType(Field field) {
    return Optional.of(field.getGenericType())
            .flatMap(v -&gt; cast(v, ParameterizedType.class))
            .flatMap(v -&gt; filterAndMap(v,
                            v1 -&gt; v1.getRawType() == MyTypeWrapper.class,
                            v1 -&gt; v1.getActualTypeArguments()[0]))
            .flatMap(v -&gt; cast(v, Class.class));
}


Finally, your collectMyTypes() method will just do a simple filter on the resulting Optional wrapper instances:

@SuppressWarnings("unchecked")
static Stream&lt;Class&lt;? extends MyType&gt;&gt; collectMyTypes(Object o) {
    return Reflection.lineage(o.getClass())
            .flatMap(c -&gt; Arrays.stream(c.getDeclaredFields()))
            .map(YourClass::extractMyType).filter(Optional::isPresent)
            .map(Optional::get);
}