在Java中运行时获取所有声明的子类?

时间:2015-04-13 15:49:35

标签: java reflection subclass

假设我有一个包含'Device'的所有子类的包,如下所示:

class TV extends Device{

   @override
   public void run() {
       //code goes here
   }

   private void moreMethods(String args){
       // more code
   }

}

在主类中,应该可以通过调用每个人的'run'方法在运行时实例化并运行Device的每个子类。

这样做的好处是允许用户简单地将新的Device文件放入包中,并且所述文件将自动运行,而无需编辑主类。

有干净的方法吗?我在想,如果我在每个子类名称的包中都有一个txt文件,那么就有可能。用户必须编辑文件并添加新设备名称的轻微不便。

有没有其他方法可以达到相同的效果?我有兴趣为用户提供一种方法来添加新的设备文件,而无需编辑现有的代码。

4 个答案:

答案 0 :(得分:1)

如果反射是一个选项,您可以先找到专用包的所有类(Can you find all classes in a package using reflection?How to get all classes names in a package?),然后使用:

for (Class<?> candidateClass : foundClasses) {
    if (Device.class.isAssignableFrom(candidateClass)) {
        candidateClass.newInstance().run();
    }
}

答案 1 :(得分:1)

Java Service Provider Interface。这是一种机制 - 给定接口可以找到实现此接口的所有实例。要求实现提供程序,其jar包含META-INF子目录中的文件,其名称为接口:java.lang.Runnable,并且内容为一个或多个完整的类名。

这或多或少是您使用.txt文件提出的建议。只有你可以使用单独的罐子。

您可能要求所有提供的类都带有SPI规范。将jar放在子目录plugins中,并将该路径作为Class-Path放在您自己jar的MANIFEST.MF中。如您所知,除了jar之外的类路径中也允许使用目录。


更多欺骗是使用Reflections库的替代方案。我觉得这是一种滥用行为,但它提供了你想要的东西,但效率却很高。


或者,您可以要求类应注释,以便您可以扫描所有类注释。需要一些编码工作。

答案 2 :(得分:0)

为什么你不使用Reflection这样做成本很高,而且可能容易出错。 您可以使用DeviceProvider,客户端可以在其中注册设备。 他可以通过它的类对象或它的规范名称来获取设备。 然后他可以向下转换来访问自定义方法或处理Device实例并调用常用方法。

来自Effective Java:

// Service interface
public interface Service {
... // Service-specific methods go here
}

// Service provider interface
public interface Provider {
      Service newService();
}

// Noninstantiable class for service registration and access
public class Services {

     private Services() { } // Prevents instantiation (Item 4)
     // Maps service names to services
     private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
     public static final String DEFAULT_PROVIDER_NAME = "<def>";
     // Provider registration API
     public static void registerDefaultProvider(Provider p) {
          registerProvider(DEFAULT_PROVIDER_NAME, p);
     }

     public static void registerProvider(String name, Provider p){
        providers.put(name, p);
     }

      // Service access API
     public static Service newInstance() {
         return newInstance(DEFAULT_PROVIDER_NAME);
     }

     public static Service newInstance(String name) {
       Provider p = providers.get(name);
         if (p == null)
           throw new IllegalArgumentException(
           "No provider registered with name: " + name);

        return p.newService();
     }

}

优点是您可以提供默认实现并允许客户端注册其自定义实现。 JDBC DriverManager的工作方式非常相似

答案 3 :(得分:0)

另一种方法是在每个包含设备类的jar中使用文本文件中的类列表(比如META-INF / device-classes.txt)。然后,您可以按如下方式列出每个device-classes.txt文件的URL:

ClassLoader cl = Thread.currentThread().getContextClassLoader();
Enumeration<URL> enm = cl.findResources("META-INF/device-classes.txt");
while (enm.hasMoreElements()) {
    runAllClassesIn(enm.nextElement());
}