如何轻松遍历所有 super classes/interfaces

How to easily iterate over all super classes/interfaces

遍历给定 Class<?> 对象的所有超 classes/interfaces 最优雅的方法是什么?我基本上是在寻找一种递归 getSuperclass() and getInterfaces() 方法,收集整个 class 层次结构。

让我们假设我们的继承如下(其中 Cn 是 classes,In 是接口):

         I3
        ↗  ↖
C2    I1    I2
  ↖     ↖  ↗
   C1    I0
     ↖  ↗ 
      C0

它应该捕获上面显示的所有 classes。如果可能的话,迭代顺序应该是breadth first,所以像这样:

C0 -> C1 -> I0 -> C2 -> I1 -> I2 -> I3

是否有提供创建 Collection<Class<?>>Stream<Class<?>>Iterator<Class<?>> 的内置方法或库?

感谢任何帮助。

此解决方案实现了 Iterator<Class<?>>。如果您可以使用库,我建议您查看 .

public static class HierarchyIterator implements Iterator<Class<?>> {
    private Queue<Class<?>> remaining = new LinkedList<>();
    private Set<Class<?>> visited = new LinkedHashSet<>();

    public HierarchyIterator(Class<?> initial) {
        append(initial);
    }

    private void append(Class<?> toAppend) {
        if (toAppend != null && !visited.contains(toAppend)) {
            remaining.add(toAppend);
            visited.add(toAppend);
        }
    }

    @Override
    public boolean hasNext() {
        return remaining.size() > 0;
    }

    @Override
    public Class<?> next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        Class<?> polled = remaining.poll();
        append(polled.getSuperclass());
        for (Class<?> superInterface : polled.getInterfaces()) {
            append(superInterface);
        }
        return polled;
    }
}

如果您需要 Collection<Class<?>>,您可以使用 Google Guava 用于:

public static Set<Class<?>> getClassHierarchy(Class<?> forClass) {
    Set<Class<?>> result = new LinkedHashSet<>();
    Iterators.addAll(result, new HierarchyIterator(forClass));
    return result;
}

呼叫:

System.out.println(getClassHierarchy(LinkedList.class));

产量

[class java.util.LinkedList, class java.util.AbstractSequentialList, interface java.util.List, interface java.util.Deque, interface java.lang.Cloneable, interface java.io.Serializable, class java.util.AbstractList, interface java.util.Collection, interface java.util.Queue, class java.util.AbstractCollection, interface java.lang.Iterable, class java.lang.Object] 

我或多或少把你的问题当作一个游戏,我注意到 广度优先 要求不是强制性的,所以这是我的解决方案。

  1. 它使用反射。
  2. 它使用递归。
  3. 它使用函数式编程。
  4. 不是很长。
  5. 它实现了一个内部迭代器 - forEach 风格。
  6. 你需要 Java 9 来编译它。
  7. 这只是一个编程游戏:)

public class ClassIterator {
    public void forEachSuperclasses(final Class<?> initialClass, final Consumer<Class<?>> action) {
        generateStream(initialClass).distinct().forEach(action);
    }

    private Stream<Class<?>> generateStream(final Class<?> clazz) {
        if (clazz == null) {
            return Stream.empty();
        }
        return Stream.concat(
               Stream.concat(Stream.of(clazz), generateStream(clazz.getSuperclass())),
                    Arrays.stream(clazz.getInterfaces()).flatMap(this::generateStream));
    }
}

如何称呼:

interface I3 {};
class C2 implements I3 {};
interface I1 extends I3 {};
interface I2 {};
class C1 extends C2 {};
interface I0 extends I0, I0 {};
class C0 extends C1 implements I0 {};

void testForEachSuperclasses() {
    final ClassIterator iterator = new ClassIterator();
    iterator.forEachSuperclasses(C1.class, System.out::println);
}

输出:

class com.example.classiterator.C0
class com.example.classiterator.C1
class com.example.classiterator.C2
class java.lang.Object
interface com.example.classiterator.I3
interface com.example.classiterator.I0
interface com.example.classiterator.I1
interface com.example.classiterator.I2

鉴于您似乎已经在使用 Guava,这里有一个使用 Guava 的图形遍历实用程序的解决方案。

public static Iterable<Class<?>> getClassHierarchy(Class<?> baseClass) {

    return Traverser.forGraph(
        (SuccessorsFunction<Class<?>>) node -> {
            Class<?> superclass = node.getSuperclass();
            List<Class<?>> interfaces = Arrays.asList(node.getInterfaces());
            return superclass == null ? interfaces
                : Iterables.concat(interfaces, Collections.singleton(superclass));
        }
    ).breadthFirst(baseClass);
}

这是一个快速的广度优先层次结构横向图:

public class ClassHierarchy {
    private Queue<Class<?>> queue;
    //a collection of "visited" classes,
    //which is also the result of the search
    private Set<Class<?>> visited;

    public Set<Class<?>> getClassHierarchy(Class<?> cls){
        visited = new LinkedHashSet<>(); //initialize visited log
        bfs(cls);
        return visited;
    }

    //breadth first traverse on hierarchy
    private void bfs(Class<?> cls) {

        if(cls == null){ return; }
        queue = new LinkedList<>(); //initialize queue
        queue.add(cls);
        while (! queue.isEmpty()) {
           cls = queue.poll();
           //loop over super classes
           for(Class<?> nextClass : getSuperClasses(cls)){
                if((nextClass != null) &&  visited.add(nextClass)) {
                     queue.add(nextClass); //add class to the queue
                }
           }
        }
        return;
    }

    private List<Class<?>> getSuperClasses(Class<?> cls) {
         List<Class<?>> superCs = new ArrayList<>();
         superCs.addAll(Arrays.asList(cls.getInterfaces()));
         superCs.add(cls.getSuperclass());
         return superCs;
    }

    private boolean isVisited(Class<?> cls) {
        return !(visited.add(cls));
    }

    public static void main(String[] args) {
        ClassHierarchy ch = new ClassHierarchy();
        System.out.println(ch.getClassHierarchy(LinkedList.class));
    }
}

(请仔细查看,我还没来得及调试完善,以后再看)

这是另一个没有外部库的解决方案,但它仍然可以懒惰地工作并且每个 class 恰好访问一次:

public static Iterable<Class<?>> getClassHierarchy(Class<?> baseClass) {

    return () -> new Iterator<Class<?>>() {

        private Class<?> nextValue;
        private Queue<Class<?>> remaining = new LinkedList<>(Collections.singleton(baseClass));
        private Set<Class<?>> visited = new HashSet<>();


        @Override
        public boolean hasNext() {
            while (nextValue == null && !remaining.isEmpty()) {
                Optional.ofNullable(remaining.poll())
                        .ifPresent((Class<?> type) -> {
                            visited.add(type);
                            Stream.concat(
                                streamOptional(Optional.ofNullable(type.getSuperclass())),
                                Arrays.stream(type.getInterfaces())
                            ).filter(visited::add)
                             .forEach(remaining::offer);
                            nextValue = type;
                        });
            }
            return nextValue != null;
        }

        private <T> Stream<T> streamOptional(final Optional<T> optional) {
            return optional.map(Stream::of).orElse(Stream.empty());
        }

        @Override
        public Class<?> next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            Class<?> value = this.nextValue;
            this.nextValue = null;
            return value;
        }
    };

}

注意:写这个很痛苦(在Java 8),因为不幸的是,没有Optional.stream()方法并且Stream.generate(supplier)无法终止所以我不能使用它。