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

2022-08-31 07:15:39

我想做这样的事情:

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

因此,我想查看应用程序世界中的所有类,当我找到一个来自 Animal 的类时,我想创建一个该类型的新对象并将其添加到列表中。这使我无需更新事物列表即可添加功能。我可以避免以下情况:

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

使用上述方法,我可以简单地创建一个扩展Animal的新类,它将自动被拾取。

更新: 10/16/2008 9:00 a.m. 太平洋标准时间:

这个问题引起了很多很好的回答——谢谢。从回复和我的研究来看,我发现我真正想做的事情在Java下是不可能的。有一些方法,比如ddimitrov的ServiceLoader机制可以工作 - 但它们对于我想要的东西来说非常繁重,我相信我只是将问题从Java代码转移到外部配置文件中。更新 5/10/19 (11 年后!现在有几个库可以帮助解决这个问题,根据@IvanNik的答案org.reflections看起来不错。此外来自@Luke Hutchison的ClassGraph的答案看起来很有趣。答案中还有几种可能性。

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

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


答案 1

我使用 org.reflections

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

另一个例子:

public static void main(String[] args) throws IllegalAccessException, InstantiationException {
    Reflections reflections = new Reflections("java.util");
    Set<Class<? extends List>> classes = reflections.getSubTypesOf(java.util.List.class);
    for (Class<? extends List> aClass : classes) {
        System.out.println(aClass.getName());
        if(aClass == ArrayList.class) {
            List list = aClass.newInstance();
            list.add("test");
            System.out.println(list.getClass().getName() + ": " + list.size());
        }
    }
}

答案 2

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

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


推荐