18

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.

coderatchet
  • 8,120
  • 17
  • 69
  • 125
  • Why? If it's for a test, just write the various `instanceof` tests yourself. Don't rely on extra code that's only there for testing. – user207421 Feb 26 '14 at 04:27

9 Answers9

14

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
higuaro
  • 15,730
  • 4
  • 36
  • 43
  • This is excellent, however what if a superclass also implements an interface that the root class implements, also what happens when an interface extends another interface, it doesn't look like this function will pick up I4 and I5 in my example. – coderatchet Feb 26 '14 at 04:31
  • 2
    `getAllExtendedOrImplementedTypesRecursivelyThatWeBelieveToBeUsefulForTheRestOfTheProgramWithoutCrashingTheJvmOrBraggingAboutOurLongMethodName` – Pierre Arlaud Feb 26 '14 at 13:02
  • 1
    @ArlaudPierre jajaja XD, you're right, `getAllExtendedOrImplementedTypes` sure is enough but don't know if it's part of the OP's requirement to call it with the long name, so I tried to be consistent with the question – higuaro Feb 27 '14 at 20:02
10

There is a ClassUtils in the Apache Common Lang that have the 2 methods you want. .getAllSuperClasses() and .getAllInterfaces().

TA Nguyen
  • 453
  • 1
  • 3
  • 8
  • 2
    this seems like it would provide a viable solution, however i do not wish to depend on an entire library (even if i will eventually depend on it later). – coderatchet Feb 26 '14 at 04:35
  • 3
    It is open source and so, you can borrow it and embedded into your codes set, until the time when you are ready to accept the entire library. Good luck. – TA Nguyen Feb 26 '14 at 04:44
2

The key you want is in the Class#getSuperclass() method:

public static Set<Class<?>> stuff(Class<?> target) {
    Set<Class<?>> classesInterfaces = new HashSet<>();
    classesInterfaces.add(target);
    classesInterfaces.addAll(Arrays.asList(target.getInterfaces());

    Class<?> superClass = target.getSuperclass();
    if(superClass != null)
        classesInterfaces.addAll(stuff(superClass));
}
chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
2

In Java8

import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class ClassUtil {
    public static Set<Class<?>> getAllExtendedOrImplementedTypesRecursively(final Class<?> clazz) {
        return walk(clazz)
                .filter(Predicate.isEqual(java.lang.Object.class).negate())
                .collect(Collectors.toSet());
    }

    public static Stream<Class<?>> walk(final Class<?> c) {
        return Stream.concat(Stream.of(c),
                Stream.concat(
                        Optional.ofNullable(c.getSuperclass()).map(Stream::of).orElseGet(Stream::empty),
                        Arrays.stream(c.getInterfaces())
                ).flatMap(ClassUtil::walk));
    }
}

Test Code:

import java.util.Set;

class Test {
    public static void main(String[] args) {
        final Set<Class<?>> set = ClassUtil.getAllExtendedOrImplementedTypesRecursively(Foo.class);
        set.stream().map(Class::getName).forEach(System.out::println);
    }

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

Output:

Test$Foo
Test$Bar
Test$I2
Test$I5
Test$I1
Test$I3
Test$I4

otamega
  • 229
  • 2
  • 5
1

From the question How do you find all subclasses of a given class in Java?, this answer might be helpful:

Using the class PojoClassImpl.java you can get the super class by calling method getSuperClass(). I think that is sufficient for you to write a recursive method.

Community
  • 1
  • 1
Miguel Jiménez
  • 1,276
  • 1
  • 16
  • 24
1

If I got your question right, you want to find all the superclasses (class and interface) of a specific class. If so you can check the following solution

to find the superclasses

        Class C = getClass();
        while (C != null) {
          System.out.println(C.getSimpleName());
          C = C.getSuperclass();
        }

to find the interfaces

        C = getClass();
        for(Class adf: C.getInterfaces()){

              System.out.println(adf.getSimpleName());
        }
stinepike
  • 54,068
  • 14
  • 92
  • 112
1

It's very easy, in case your class is Foo then your code will be like this,

public void getClassDetails() {

    Class klass = Foo.class;
    Class<?> superKlass = klass.getSuperClass();
    Class[] interfaces = klass.getInterfaces();
}
ravi.patel
  • 119
  • 4
0

I have once implemented similiar mechanism using asm on some ShrinkWrap branch https://github.com/mmatloka/shrinkwrap/commit/39d5c3aa63a9bb85e6d7b68782879ca10cca273b . The issue is sometimes class might use object which is an interface implementation not mentioned in other file so it still can fail during deployment.

From what I know some time ago official position was rather not to include such feature inside of the ShrinkWrap, but rather rely on tooling e.g. JBoss Tools, where should be a feature allowing for recursive class additions.

mmatloka
  • 1,986
  • 1
  • 20
  • 46
0

Some suggested reductions on @otamega's solution:

This one does not filter Object (that would be an inconsistency) and uses some refactoring and static imports to make code more readable.

package com.stackoverflow.question22031207;

import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toSet;
import static java.util.stream.Stream.concat;
import static java.util.stream.Stream.of;
import static java.util.stream.Stream.ofNullable;

import java.util.Set;
import java.util.stream.Stream;

public class ClassUtil {
    public static Set<Class<?>> getAllExtendedOrImplementedTypesRecursively(final Class<?> clazz) {
        return walk(clazz).collect(toSet());
    }

    public static Stream<Class<?>> walk(final Class<?> clazz) {
        final Class<?> superclass = clazz.getSuperclass();
        final Class<?>[] interfaces = clazz.getInterfaces();
        return concat(of(clazz), concat(ofNullable(superclass), stream(interfaces)).flatMap(ClassUtil::walk));
    }
}
Jörn Guy Süß
  • 1,408
  • 11
  • 18