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

在运行时,查找Java应用程序中扩展基类的所有类,java,reflection,inheritance,Java,Reflection,Inheritance,我想这样做: 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() ); for (String classpathEntry : System.getProperty("java.class.path").split(

我想这样做:

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() );
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
            }
        }
    }
}
列表看起来不错。同样来自于@Luke Hutchison的作品看起来很有趣。答案中还有更多的可能性

另一种说明我想要什么的方法是:我的Animal类中的静态函数查找并实例化从Animal继承的所有类,而无需任何进一步的配置/编码。如果我必须进行配置,我也可以在Animal类中实例化它们。我理解这一点,因为Java程序只是.class文件的松散联合体,这就是它的方式


有趣的是,这似乎是在C#中。Java动态加载类,因此您的类范围将仅是那些已经加载(但尚未卸载)的类。也许您可以使用自定义类装入器来检查每个装入类的超类型。我认为没有API来查询加载的类集。

不幸的是,这不完全可能,因为类加载器不会告诉您哪些类可用。但是,您可以非常接近地执行以下操作:

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() );
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应用程序,在应用程序服务器下不起作用。

这是一个棘手的问题,您需要使用静态分析来找出这些信息,在运行时不容易获得。
基本上,获取应用程序的类路径,扫描可用的类,并读取它从哪个类继承的类的字节码信息。请注意,类别狗可能不会直接从动物继承,但可能会从宠物继承,而宠物又反过来从动物继承,因此您需要跟踪该层次结构。

感谢所有回答此问题的人

看来这确实是个难题。我最终放弃了,在我的基类中创建了一个静态数组和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文件的集合,所以运行时在引用类之前不知道该类。在那个时候,加载程序加载它——我试图做的是在引用它之前发现它,这是不可能的,如果不到文件系统并查看它的话

我总是喜欢能够发现自己的代码,而不是我必须告诉它自己,但唉,这也行得通


再次感谢

一种方法是让类使用静态初始值设定项。。。我不认为这些是遗传的(如果它们是遗传的,则不会起作用):


它要求您将此代码添加到所有涉及的类中。但它避免了在某个地方出现一个丑陋的大循环,测试每个班级寻找动物的孩子。

从面向方面的角度思考这个问题;实际上,您要做的是在运行时了解扩展了Animal类的所有类。(我认为这比你的标题更准确地描述了你的问题;否则,我认为你没有运行时问题。)

因此,我认为您需要创建基类(Animal)的构造函数,它将当前实例化的类的类型添加到静态数组中(我更喜欢ArrayList,但每个都是自己的)

所以大致上,

public abstract class Animal
    {
    private static ArrayList<Class> instantiatedDerivedTypes;
    public Animal() {
        Class derivedClass = this.getClass();
        if (!instantiatedDerivedClass.contains(derivedClass)) {
            instantiatedDerivedClass.Add(derivedClass);
        }
    }
公共抽象类动物
{
私有静态ArrayList实例化的DelivedTypes;
公共动物(){
Class-derivedClass=this.getClass();
如果(!实例化的derivedClass.contains(derivedClass)){
实例化derivedClass.Add(derivedClass);
}
}

当然,在Animal上需要一个静态构造函数来初始化实例化的DerivedClass…我认为这可能会满足您的需要。请注意,这取决于执行路径;如果您有一个从Animal派生而来的Dog类从未被调用,那么您的Animal类列表中就不会有它。

实现您所需的Java方法是使用该机制


此外,许多人通过在一个众所周知的类路径位置(即/META-INF/services/myplugin.properties)中拥有一个文件,然后使用枚举所有jar中具有此名称的所有文件来实现自己的滚动。这允许每个jar导出自己的提供者,并且您可以使用Class.forName()通过反射来实例化它们

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

实现只需创建一个package-info.java,并将magic注释与它们想要支持的类列表放在一起。

我使用:

Reflections=newreflections(“com.mycompany”);
设置您可以从
如果您需要一些简单而快速的东西,而无需重构任何现有代码

下面是一个未加载任何类的简单示例:

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 {}
封装测试;
导入java.util.Set;
导入net.sourceforge.stripes.util.ResolverUtil;
公共类基类测试{
公共静态void main(字符串[]args)引发异常{
ResolverUtil resolver=新ResolverUtil();
分解器。FindImplements(动物级,“测试”);
使用设置,您可以执行以下操作:

List<Animal> animals = new ArrayList<Animal>();
animals.add( new Dog() );
animals.add( new Cat() );
animals.add( new Donkey() );
...
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));
}
String package=“com.mycompany”;
列出动物=新建ArrayList();
for(PojoClass PojoClass:PojoClassFactory.enumerateClassesByExtendingType)(包,Animal.class,null){
add((Animal)InstanceFactory.getInstance(pojoClass));
}
使用此

public static Set<Class> getExtendedClasses(Class superClass)
{
    try
    {
        ResolverUtil resolver = new ResolverUtil();
        resolver.findImplementations(superClass, superClass.getPackage().getName());
        return resolver.getClasses();  
    }
    catch(Exception e)
    {Log.d("Log:", " Err: getExtendedClasses() ");}

    return null;
}

getExtendedClasses(Animals.class);
公共静态集getExtendedClasses(类超类)
{
尝试
{
ResolverUtil resolver=新ResolverUtil();
解析程序.findImplements(超类,超类.getPackage().getName());
返回resolver.getClasses();
}
捕获(例外e)
{Log.d(“Log:,“Err:getExtendedClasses()”);}
返回null;
}
getExtendedClasses(一个
public static Set<Class> getExtendedClasses(Class superClass)
{
    try
    {
        ResolverUtil resolver = new ResolverUtil();
        resolver.findImplementations(superClass, superClass.getPackage().getName());
        return resolver.getClasses();  
    }
    catch(Exception e)
    {Log.d("Log:", " Err: getExtendedClasses() ");}

    return null;
}

getExtendedClasses(Animals.class);
List<Class<Animal>> animals;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("com.zoo.animals")
        .enableClassInfo().scan()) {
    animals = scanResult
        .getSubclasses(Animal.class.getName())
        .loadClasses(Animal.class);
}