javaclassjboss-arquillian

find all classes and interfaces a class extends or implements recursively


I was wondering if there was an easy way of determining the complete list of Types that a Java class extends or implements recursively?

for instance:

class Foo extends Bar implements I1, I2 {...}
class Bar implements I3 {...}
interface I1 extends I4, I5 {...}
interface I2 {...}
interface I3 {...}
interface I4 {...}
interface I5 {...}

class ClassUtil {
    public static Set<Class<?>> getAllExtendedOrImplementedTypesRecursively(Class<?> clazz){
        ???
    }
}

import static org.junit.Assert.*;
public class ClassUtilTest {
    @Test
    public void shouldEqualClasses(){
        Set<Class<?>> types = ClassUtil.getAllExtendedOrImplementedTypesRecursively(Foo.class);
        Set<Class<?>> checklist = new HashSet<>();
        checklist.add(Foo.class);
        checklist.add(Bar.class);
        checklist.add(I1.class);
        checklist.add(I2.class);
        checklist.add(I3.class);
        checklist.add(I4.class);
        checklist.add(I5.class);
        assertTrue(checklist.containsAll(types));
        assertTrue(types.containsAll(checklist));
    }
}

Think Arquillian ShrinkWrap creation helper.

UPDATE: due to the Class object not implementing Comparable> I also need to find a way of creating a Set (or similar class) without implementing the Comparable interface (for instance, solely relying on the hashcode of the class object).

UPDATE: changed the test to use hashset. derp.


Solution

  • The following implementation of the method does what the OP requires, it traverses the inheritance hierarchy for every class and interface:

    public static Set<Class<?>> getAllExtendedOrImplementedTypesRecursively(Class<?> clazz) {
        List<Class<?>> res = new ArrayList<>();
    
        do {
            res.add(clazz);
    
            // First, add all the interfaces implemented by this class
            Class<?>[] interfaces = clazz.getInterfaces();
            if (interfaces.length > 0) {
                res.addAll(Arrays.asList(interfaces));
    
                for (Class<?> interfaze : interfaces) {
                    res.addAll(getAllExtendedOrImplementedTypesRecursively(interfaze));
                }
            }
    
            // Add the super class
            Class<?> superClass = clazz.getSuperclass();
    
            // Interfaces does not have java,lang.Object as superclass, they have null, so break the cycle and return
            if (superClass == null) {
                break;
            }
    
            // Now inspect the superclass 
            clazz = superClass;
        } while (!"java.lang.Object".equals(clazz.getCanonicalName()));
    
        return new HashSet<Class<?>>(res);
    }    
    

    I tested with JFrame.class and I got the following:

    Set<Class<?>> classes = getAllExtendedOrImplementedTypesRecursively(JFrame.class);
    for (Class<?> clazz : classes) {
        System.out.println(clazz.getName());
    }
    

    Output:

    java.awt.Container
    java.awt.Frame
    javax.swing.JFrame
    javax.swing.TransferHandler$HasGetTransferHandler
    java.awt.Window
    javax.accessibility.Accessible
    javax.swing.RootPaneContainer
    java.awt.Component
    javax.swing.WindowConstants
    java.io.Serializable
    java.awt.MenuContainer
    java.awt.image.ImageObserver
    

    UPDATE: For the OP's test case it prints:

    test.I5
    test.Bar
    test.I2
    test.I1
    test.Foo
    test.I3
    test.I4