Java8 Streaming a class hierarchy

java-8, java-stream, reflection

Solution

In Java 9, you would probably use

public static Stream<Class<?>> streamSuperclass(Class<?> type) {
    return Stream.iterate(type, Objects::nonNull, Class::getSuperclass);
}

but in Java 8, this feature is not available, so you may resort to implement the Stream manually:

public static Stream<Class<?>> streamSuperclass(Class<?> type) {
    return StreamSupport.stream(
        new Spliterators.AbstractSpliterator<Class<?>>(100L,
            Spliterator.ORDERED|Spliterator.IMMUTABLE|Spliterator.NONNULL) {
            Class<?> current = type;
            public boolean tryAdvance(Consumer<? super Class<?>> action) {
                if(current == null) return false;
                action.accept(current);
                current = current.getSuperclass();
                return true;
            }
        }, false);
}

Note that this will stream from the most specific type towards `java.lang.Object`. If you want the order to be from `Object` to the most specific one, there is no way around gathering the elements first, whether recursive or iterative, is not so important, but `Stream.concat` is indeed the least performance variant. You can simply use

public static Stream<Class<?>> streamSuperclass(Class<?> type) {
    return reverse(Stream.<Class<?>>builder(), type, Class::getSuperclass).build();
}
private static <T> Stream.Builder<T> reverse(
        Stream.Builder<T> builder, T t, UnaryOperator<T> op) {
    return t==null? builder: reverse(builder, op.apply(t), op).add(t);
}

The iterative variant also is not so bad:

public static Stream<Class<?>> streamSuperclass(Class<?> type) {
    List<Class<?>> l=new ArrayList<>();
    for(; type!=null; type=type.getSuperclass()) l.add(type);
    Collections.reverse(l);
    return l.stream();
}

For streams as small as a typical class hierarchy, an `ArrayList` is not worse than a `Stream.Builder` and for very large streams, filling the builder using a recursion might not be the best solution either…

Problem

I am slowly learning the new Java 8 features and I am trying to find a way to process a class hierarchy (from child to parent) as a stream. For instance find an annotation on a class or it's parents. Before Java 8, I would have done it this way : ``` public static <T extends Annotation> T getAnnonationOn(Class<?> type, Class<T> annType) { Class<?> t = type; T annot = null; while (t != null && annot == null) { annot = t.getAnnotation(annType); t = t.getSuperclass(); } return annot; } ``` Now I wish to do it with a more "functional programming" way. I could not find a better way than concatenate streams with a recursive like follow : ``` import java.lang.annotation.Annotation; import java.util.stream.Stream; public static <T extends Annotation> T getAnnonationOn(Class<?> type, Class<T> annType) { return ClassIterator.streamSuperclass(type) .map(t -> t.getAnnotation(annType)) .filter(a -> a != null) .findFirst() .orElse(null); } public static class ClassIterator { public static Stream<Class<?>> streamSuperclass(Class<?> type) { if (type.getSuperclass() != null) { return Stream.concat(Stream.of(type), Stream.of(type.getSuperclass()).flatMap(ClassIterator::streamSuperclass)); } return Stream.of(type); } } ``` But I am not quite satisfied of the solution. Although I did not benchmark it I think the stream concatenation is quite cumbersome and under performant. Is there a better way to turn a recursive into a stream ?

Original source

Related problems