2014-02-26 5 views
16

Ich frage mich, ob es eine einfache Möglichkeit gibt, die vollständige Liste der Typen zu bestimmen, die eine Java-Klasse rekursiv erweitert oder implementiert?finde alle Klassen und Schnittstellen, die eine Klasse rekursiv erweitert oder implementiert

zum Beispiel:

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

Denken Arquillian ShrinkWrap Schaffung Helfer.

UPDATE: aufgrund der Klasse Objekt nicht vergleichbar Implementierung> Ich brauche auch ohne die Umsetzung die Schnittstelle Comparable ein Set (oder Klasse) zu schaffen, einen Weg finden (zum Beispiel allein auf dem Hash-Code der Klasse unter Berufung Objekt).

UPDATE: änderte den Test, um hashset zu verwenden. derp.

+0

Warum? Wenn es für einen Test ist, schreiben Sie einfach die verschiedenen 'instanceof'-Tests selbst. Verlassen Sie sich nicht auf zusätzlichen Code, der nur zum Testen zur Verfügung steht. – EJP

Antwort

11

Die folgende Implementierung des Verfahrens tut, was der OP erfordert, ist es das Erbe quert Hierarchie für jede Klasse und Schnittstelle:

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 tes ted mit JFrame.class und ich bekam die folgende:

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

Ausgang:

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: für den Testfall des OP druckt:

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

Das ist hervorragend, aber was passiert, wenn eine Oberklasse auch eine Schnittstelle implementiert, die die Root-Klasse implementiert, auch was passiert, wenn eine Schnittstelle eine andere Schnittstelle erweitert, sieht es nicht so aus, als würde diese Funktion I4 und I5 in meinem Beispiel übernehmen. – coderatchet

+1

'getAllExtendedOrImplementedTypesRecursivelyThatWeBelieveToBeUsefulForTheRestOfTheProgramWithoutCrashingTheJvmOrBraggingAboutOurLongMethodName' –

+1

@ArlaudPierre jajaja XD, du hast Recht,' getAllExtendedOrImplementedTypes' sicher genug ist, aber weiß nicht, ob es ein Teil der Anforderung des OP ist es mit dem langen Namen zu nennen, so habe ich versucht, mit der Frage, konsequent zu sein – higuaro

10

Es gibt ein ClassUtils in Apache Common Lang, die die 2 Methoden haben, die Sie wünschen. .getAllSuperClasses() und .getAllInterfaces().

+2

Dies scheint eine brauchbare Lösung zu sein, aber ich möchte nicht auf eine ganze Bibliothek angewiesen sein (auch wenn ich später davon abhängig sein werde). – coderatchet

+3

Es ist Open-Source und so können Sie es ausleihen und in Ihrem Codesatz eingebettet, bis zu dem Zeitpunkt, wenn Sie bereit sind, die gesamte Bibliothek zu akzeptieren. Viel Glück. –

2

Der Schlüssel Sie wollen, ist in der Class#getSuperclass() Methode:

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

Wenn ich Ihre Frage richtig beantwortet habe, möchten Sie alle Superklassen (Klasse und Schnittstelle) einer bestimmten Klasse finden. Wenn so dass Sie die folgende Lösung überprüfen

, um die übergeordneten Klassen finden

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

zu finden die Schnittstellen

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

       System.out.println(adf.getSimpleName()); 
     } 
1

Es ist sehr einfach, für den Fall, Ihre Klasse ist Foo, dann wird Ihr Code wie folgt sein:

public void getClassDetails() { 

    Class klass = Foo.class; 
    Class<?> superKlass = klass.getSuperClass(); 
    Class[] interfaces = klass.getInterfaces(); 
} 
+1

Dies geht nicht rekursiv – coderatchet

0

Ich habe einmal ähnlichen Mechanismus mit asm auf einigen ShrinkWrap Zweig https://github.com/mmatloka/shrinkwrap/commit/39d5c3aa63a9bb85e6d7b68782879ca10cca273b implementiert.Das Problem besteht manchmal darin, dass die Klasse ein Objekt verwenden könnte, das eine Schnittstellenimplementierung ist, die in anderen Dateien nicht erwähnt wird, so dass es während der Bereitstellung immer noch fehlschlagen kann.

Von dem, was ich weiß vor einiger Zeit offizielle Position war eher nicht zu solchen Merkmal innerhalb der ShrinkWrap gehören, sondern eher auf Werkzeug verlassen, z. , wo ein Feature sein sollte, das rekursive Klassenzugaben erlaubt.

0

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

Testcode:

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 {} 
} 

Ausgang:

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