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 ?