1

How to create a Stream of class ancestors?

What I can do is:

public static Stream<Class<?>> streamOfAncestors(Class<?> klass) {
    ArrayList<Class<?>> ancestorsList = new ArrayList<>();
    while (true) {
        klass = klass.getSuperclass();
        if (klass == null) {
            return ancestorsList.stream();
        } else {
            ancestorsList.add(klass);
        }
    }
}

But it is not elegant and my stream is not lazy. How to make a lazy Stream?

Eran
  • 387,369
  • 54
  • 702
  • 768
tomto
  • 73
  • 1
  • 5

1 Answers1

2

One option is to use Stream.iterate(). You'll have to combine it with takeWhile (introduced in Java 9) in order to make the Stream finite.

public static Stream<Class<?>> streamOfAncestors(Class<?> klass) {
    return Stream.iterate (klass.getSuperclass(), k -> k != null ? k.getSuperclass() : null)
                 .takeWhile(Objects::nonNull);
}

Or, if you don't want to use Java 9 features, you can write a method similar to Stream.iterate(), but make it produce a finite Stream:

public static Stream<Class<?>> streamOfAncestors(Class<?> klass) {
    final Iterator<Class<?>> iterator = new Iterator<Class<?>>() {
        @SuppressWarnings("unchecked")
        Class<?> k = klass.getSuperclass ();

        @Override
        public boolean hasNext() {
            return k != null;
        }

        @Override
        public Class<?> next() {
            Class<?> curr = k;
            k = k.getSuperclass ();
            return curr;
        }
    };
    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
            iterator,
            Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
}

Sample execution of the second method:

streamOfAncestors (StringBuilder.class).forEach (System.out::println);

Output:

class java.lang.AbstractStringBuilder
class java.lang.Object

EDIT:

As Holger commented, all of this is not necessary if you are using Java 9, which has a Stream.iterate variant that produces a finite Stream.

All you need to write is:

public static Stream<Class<?>> streamOfAncestors(Class<?> klass) {
    return Stream.iterate (klass.getSuperclass(), Objects::nonNull, Class::getSuperclass);
}
Eran
  • 387,369
  • 54
  • 702
  • 768
  • 1
    Whenever you feel tempted to chain `takeWhile` to `iterate`, remember the three-arg `iterate` method, also introduced in Java 9, i.e. `Stream.iterate(klass, Objects::nonNull, Class::getSuperclass)`. And, as often, implementing a `Spliterator` directly turns out to be simpler than implementing and wrapping an `Iterator` (see the dup link)… – Holger Jan 25 '18 at 11:34
  • @Holger Thanks. I was surprised I didn't find a `Stream.iterate()` variant for a finite Stream in Java 8, and didn't think to check if they added one in Java 9 (I should probably install Java 9 on my machine...). – Eran Jan 25 '18 at 11:38