2014-02-26 65 views
16

我想知道是否有一種簡單的方法來確定Java類擴展或實現遞歸的類型的完整列表?找到所有類和接口一個類擴展或實現遞歸

例如:

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

想到的Arquillian拆封創建幫手。因爲Class對象沒有實現Comparable>我還需要找到一種創建Set(或類似的類)而不實現Comparable接口的方法(例如,完全依賴類的哈希碼目的)。

更新:更改測試使用哈希集。 DERP。

+0

爲什麼?如果是爲了測試,只需自己編寫各種'instanceof'測試。不要依賴額外的代碼來進行測試。 – EJP

回答

11

該方法的以下實現做什麼的任擇議定書要求,它遍歷繼承每個類別和接口的層次結構:

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泰德與JFrame.class和我有以下幾點:

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

輸出:

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:對於OP的測試情況下,它會打印:

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

這很好,但是如果一個超類還實現了一個根類實現的接口,當接口擴展另一個接口時會發生什麼,它看起來不像這個函數會在我的例子中選擇I4和I5。 – coderatchet

+1

'getAllExtendedOrImplementedTypesRecursivelyThatWeBelieveToBeUsefulForTheRestOfTheProgramWithoutCrashingTheJvmOrBraggingAboutOurLongMethodName' –

+1

@ArlaudPierre JAJAJA XD,你說得對,'getAllExtendedOrImplementedTypes'肯定是不夠好,但不知道這是否是的OP的要求與長名字來稱呼它的一部分,所以我試圖與這個問題相一致 – higuaro

10

Apache Common Lang中有一個ClassUtils具有您想要的兩種方法。 .getAllSuperClasses()和.getAllInterfaces()。

+2

這看起來似乎會提供一個可行的解決方案,但我不想依賴整個圖書館(即使我以後最終會依賴它)。 – coderatchet

+3

它是開源的,所以你可以借用它並嵌入你的代碼集,直到你準備好接受整個圖書館。祝你好運。 –

2

你想要的關鍵是在Class#getSuperclass()方法:

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

如果我的問題得到了解決,您希望查找特定類的所有超類(類和接口)。如果是這樣你可以檢查以下解決方案

找到超

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

找到接口

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

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

這很容易,萬一你的班級是Foo,那麼你的代碼將是這樣的,

public void getClassDetails() { 

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

這不會遞歸下去 – coderatchet

0

我曾經在ShrinkWrap分支https://github.com/mmatloka/shrinkwrap/commit/39d5c3aa63a9bb85e6d7b68782879ca10cca273b上實現了使用asm的類似機制。問題是有時候類可能會使用對象,這是一個在其他文件中沒有提到的接口實現,所以它在部署過程中仍然可能會失敗。

從我以前知道的官方位置來說,寧可不在ShrinkWrap的範圍內包含這樣的特徵,而是依賴於工具,例如, JBoss Tools,其中應該是允許遞歸類添加的功能。

0

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

測試代碼:

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

輸出:

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

相關問題