问题

我想做这样的事情:

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

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

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

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

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

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

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

有趣的是,这似乎是fairly trivial在C#中。


#1 热门回答(131 赞)

我用的是68661073:

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

#2 热门回答(32 赞)

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

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


#3 热门回答(8 赞)

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

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

所以粗略地说;

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

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


原文链接