如何轻松迭代所有超类/接口

时间:2018-04-16 15:51:06

标签: java iteration breadth-first-search

迭代给定Class<?>对象的所有超类/接口的最优雅方法是什么?我基本上在寻找递归getSuperclass()getInterfaces()方法,收集整个类层次结构。

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

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

它应该捕获上面显示的所有类。如果可能,迭代顺序应为breadth first,如下所示:

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

是否有内置方法或图书馆可以创建Collection<Class<?>>Stream<Class<?>>Iterator<Class<?>>

任何帮助都是有意义的。

5 个答案:

答案 0 :(得分:3)

此解决方案实现Iterator<Class<?>>。如果你可以使用库,我建议你查看the accepted answer

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 :(得分:3)

鉴于您似乎已经使用了Guava,这是使用Guava的Graph Traversal实用程序的解决方案。

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);
}

答案 2 :(得分:2)

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

  1. 它使用反射。
  2. 它使用递归。
  3. 它使用函数式编程。
  4. 时间不长。
  5. 它实现了一个内部迭代器 - forEach样式。
  6. 您需要Java 9来编译它。
  7. 这只是一个编程游戏:)
  8. 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
    

答案 3 :(得分:1)

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

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));
    }
}

(请仔细检查。我没有时间调试和改进。稍后会再看一下)

答案 4 :(得分:1)

这是另一个没有外部库的解决方案,但它仍然可以懒惰地运行并且每个类只访问一次:

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)无法终止,所以我无法使用它。