在运行时,查找扩展基类的Java应用程序中的所有类

我想要做这样的事情:

List<Animal> animals = new ArrayList<Animal>(); for( Class c: list_of_all_classes_available_to_my_app() ) if (c is Animal) animals.add( new c() ); 

所以,我想看看我的应用程序的所有的类,当我find一个从动物下降,我想创build一个新types的对象,并将其添加到列表中。 这使我可以添加function,而不必更新的东西列表。 我可以避免以下情况:

 List<Animal> animals = new ArrayList<Animal>(); animals.add( new Dog() ); animals.add( new Cat() ); animals.add( new Donkey() ); ... 

通过上面的方法,我可以简单地创build一个扩展Animal的新类,它会自动拾取。

更新date:2008年10月16日上午9:00太平洋标准时间:

这个问题产生了很多很好的答案 – 谢谢。 从答复和我的研究中,我发现我真正想要做的事情在Java下是不可能的。 有一些方法,比如ddimitrov的ServiceLoader机制可以工作 – 但是对于我想要的,它们非常沉重,我相信我只是将问题从Java代码移动到外部configuration文件。

另一种expression我想要的方式:我的Animal类中的静态函数查找并实例化所有inheritance自Animal的类 – 不需要任何进一步的configuration/编码。 如果我必须configuration,不妨将它们在Animal类中实例化。 我明白,因为一个Java程序只是一个松散的.class文件联合,就是这样。

有趣的是,在C#中这似乎相当微不足道 。

我使用org.reflections :

 Reflections reflections = new Reflections("com.mycompany"); Set<Class<? extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class); 

Java方式做你想做的就是使用ServiceLoader机制。

还有许多人通过在一个众所周知的类path位置(即/META-INF/services/myplugin.properties)中有一个文件,然后使用ClassLoader.getResources()从所有jar中枚举具有该名称的所有文件来自己推出自己的文件。 这允许每个jar导出自己的提供者,你可以使用Class.forName()来reflection实例化它们。

从面向方面的angular度思考这个问题; 你真正想做的是知道所有在运行时已经扩展了Animal类的类。 (我认为这比你的标题更准确地描述了你的问题;否则,我不认为你有一个运行时问题。)

所以我想你想要的是创build一个基类(动物)的构造函数,它添加到您的静态数组(我喜欢ArrayLists,我自己,但每个他们自己)当前类的实例化的types。

所以粗略地说

 public abstract class Animal { private static ArrayList<Class> instantiatedDerivedTypes; public Animal() { Class derivedClass = this.getClass(); if (!instantiatedDerivedClass.contains(derivedClass)) { instantiatedDerivedClass.Add(derivedClass); } } 

当然,你需要动物的静态构造函数来初始化instantiatedDerivedClass …我想这会做你可能想要的。 请注意,这是执行path相关的; 如果你有一个从动物派生出来的Dog类,它永远不会被调用,你不会在动物类列表中find它。

不幸的是,这不是完全可能的,因为ClassLoader不会告诉你什么类可用。 但是,你可以相当接近地做这样的事情:

 for (String classpathEntry : System.getProperty("java.class.path").split(System.getProperty("path.separator"))) { if (classpathEntry.endsWith(".jar")) { File jar = new File(classpathEntry); JarInputStream is = new JarInputStream(new FileInputStream(jar)); JarEntry entry; while( (entry = is.getNextJarEntry()) != null) { if(entry.getName().endsWith(".class")) { // Class.forName(entry.getName()) and check // for implementation of the interface } } } } 

编辑: johnstok是正确的(在评论中),这只适用于独立的Java应用程序,并不会在应用程序服务器下工作。

Javadynamic加载类,所以你的宇宙类将只有那些已经加载(还没有卸载)。 也许你可以用一个自定义的类加载器来做些事情,它可以检查每个加载类的超types。 我不认为有一个API来查询加载类的集合。

谢谢所有回答这个问题的人。

看来这确实是一个棘手的问题。 我最终放弃了,并在我的基类中创build一个静态数组和getter。

 public abstract class Animal{ private static Animal[] animals= null; public static Animal[] getAnimals(){ if (animals==null){ animals = new Animal[]{ new Dog(), new Cat(), new Lion() }; } return animals; } } 

Java似乎并没有像C#那样设置为自我发现。 我想问题是,因为Java应用程序只是一个目录/ jar文件中的.class文件的集合,运行时直到它被引用才知道类。 当时加载器加载它 – 我想要做的是发现它之前,我参考它是不可能的,没有去文件系统和寻找。

我总是喜欢能够发现自己的代码,而不是我必须告诉它自己,但是这也可以。

再次感谢!

你可以使用Stripes框架的 ResolverUtil ( raw source )
如果你需要一些简单而快速的东西,而不需要重构现有的代码。

这里有一个简单的例子,没有加载任何类:

 package test; import java.util.Set; import net.sourceforge.stripes.util.ResolverUtil; public class BaseClassTest { public static void main(String[] args) throws Exception { ResolverUtil<Animal> resolver = new ResolverUtil<Animal>(); resolver.findImplementations(Animal.class, "test"); Set<Class<? extends Animal>> classes = resolver.getClasses(); for (Class<? extends Animal> clazz : classes) { System.out.println(clazz); } } } class Animal {} class Dog extends Animal {} class Cat extends Animal {} class Donkey extends Animal {} 

这也适用于应用程序服务器,因为这是devise工作的地方;)

代码基本上做到以下几点:

  • 遍历指定的包中的所有资源
  • 只保留以.class结尾的资源
  • 使用ClassLoader#loadClass(String fullyQualifiedName)加载这些类
  • 检查Animal.class.isAssignableFrom(loadedClass);

使用OpenPojo,您可以执行以下操作:

 String package = "com.mycompany"; List<Animal> animals = new ArrayList<Animal>(); for(PojoClass pojoClass : PojoClassFactory.enumerateClassesByExtendingType(package, Animal.class, null) { animals.add((Animal) InstanceFactory.getInstance(pojoClass)); } 

这是一个棘手的问题,您将需要使用静态分析来查找这些信息,在运行时不易获取。 基本上得到你的应用程序的类path,并扫描可用的类,并读取它inheritance的类的字节码信息。 请注意,一个类的狗可能不直接从动物inheritance,但可能inheritance从动物inheritance的宠物,所以你需要跟踪该层次。

一种方法是使类使用静态初始化…我不认为这些是inheritance(如果它们是不会工作的):

 public class Dog extends Animal{ static { Animal a = new Dog(); //add a to the List } 

它要求您将此代码添加到所有涉及的类中。 但它避免了一个大的丑陋的循环某处,testing每一个类寻找动物的孩子。

我使用包级别注解非常优雅地解决了这个问题,然后使这个注解有一个类的列表作为参数。

查找实现接口的Java类

实现只需创build一个package-info.java,并将魔术注释放入想要支持的类的列表中。