当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《在运行时,查找扩展基类的Java应用程序中的所有类》经验,为你挑选了4个好方法。

我想做这样的事情:

List animals = new ArrayList();

for( Class c: list_of_all_classes_available_to_my_app() )
   if (c is Animal)
      animals.add( new c() );

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

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

通过上面的方法,我可以简单地创建一个扩展Animal的新类,它将自动被拾取.

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

这个问题引起了很多很好的回应 - 谢谢.从回答和我的研究中,我发现我真正想要做的就是在Java下不可能.有一些方法,比如ddimitrov的ServiceLoader机制可以工作 - 但它们对我想要的东西非常重,我相信我只是将问题从Java代码转移到外部配置文件.

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

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



1> IvanNik..:

我使用org.reflections:

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

另一个例子:

public static void main(String[] args) throws IllegalAccessException, InstantiationException {
    Reflections reflections = new Reflections("java.util");
    Set> classes = reflections.getSubTypesOf(java.util.List.class);
    for (Class 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());
        }
    }
}


感谢关于org.reflections的链接.我错误地认为这会像在.Net世界中一样容易,这个答案为我节省了很多时间.
有没有办法获得所有反射,即没有注明特定的包,但加载所有可用的类?

2> ddimitrov..:

Java的方法是使用ServiceLoader机制.

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


实际上,如果您需要一种有效的解决方案,那么Reflections/Scannotations是一个不错的选择,但它们都强加了外部依赖,是非确定性的,并且不受Java Community Process的支持.此外,我想强调一下,你永远无法可靠地获得所有实现接口的类,因为随时都可以轻松制造出新的接口.对于它的所有瑕疵,Java的服务加载器易于理解和使用.我会因为不同的原因而远离它,但是类路径扫描更糟糕:http://stackoverflow.com/a/7237152/18187

3> Paul Sonier..:

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

所以我认为你想要的是创建一个基类(Animal)的构造函数,它添加到你的静态数组(我更喜欢ArrayLists,我自己,但更喜欢他们自己的)正在实例化的当前类的类型.

所以粗略地说;

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

当然,你需要在Animal上使用一个静态构造函数来初始化instantiatedDerivedClass ...我想这会做你可能想要的.请注意,这是依赖于执行路径的; 如果您有一个源自Animal的Dog类,它永远不会被调用,那么您将不会在Animal类列表中拥有它.



4> jonathan-sta..:

不幸的是,这并不是完全可能的,因为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应用程序,并且不适用于应用程序服务器.

推荐阅读
依然-狠幸福
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有