7

我有几个实现两个接口的类。它们都实现了BaseInterface和其他一些特定于它们的接口。

我希望能够使用下面的loadClass方法来实例化.properties文件中引用的类并调用它们都包含的通用方法(因为它们实现了 BaseInterface)。

public interface BaseInterface {
    public void doBase();
}

public interface SpecificInterface extends BaseInterface {
    public void doSpecific();
}

public class SpecificClass implements SpecificInterface {
    public void doBase() { ... }

    public void doSpecific() { ... }
}

public class LoadClass() {
    private PropertiesLoader propertiesLoader = new PropertiesLoader();

    public <C extends BaseInterface> C loadClass(String propertyName) {
        Class<C> theClass;

        // Load the class.
        theClass = propertiesLoader.getPropertyAsClass(propertyName);

        // Create an instance of the class.
        C theInstance = theClass.newInstance();

        // Call the common method.
        theInstance.doBase();

        return theInstance;
    }
}

不幸的是,当我运行代码时:

loadClassInstance.loadClass("SpecificClass");

我得到以下异常:

Exception in thread "main" java.lang.ClassCastException:
SpecificClass cannot be cast to BaseInterface
at LoadClass.loadClass

任何想法我将如何解决这个问题?

非常感谢,丹尼

4

3 回答 3

18

Java 的服务提供者接口 (SPI) 库允许您使用公共无参数构造函数根据它们实现的接口动态加载类,这一切都是通过使用META-INF/services.

首先,您需要interface

package com.example;

public interface SomeService {

    String getServiceId();

    String getDisplayName();
}

然后,当您需要它们时,您可以使用 Java 的类加载它们,ServiceLoader该类实现Iterable

ServiceLoader<SomeService> loader = ServiceLoader.load(SomeService.class);
for (SomeService serv : loader) {
    System.out.println(serv.getDisplayName());
}

然后,当您的类路径上有 1 个或多个实现类时,它们会在META-INF/services. 因此,如果您有实现:

package com.acme;

public class SomeImplementation implements SomeService {

    // ...

    public SomeImplementation() { ... }

    // ...
}

请注意,此类需要默认的无参数构造函数,这不是可选的。

META-INF/services您可以通过在类路径(例如 jar 的根目录)中创建具有以下属性的文件来向类加载器注册它:

  1. 文件名是接口的全限定类名,在这种情况下,它是com.example.SomeService
  2. 该文件包含一个以换行符分隔的实现列表,因此对于示例实现,它将包含一行:com.acme.SomeImplementation.

你去了,就是这样。你如何构建你的项目将决定你把META-INF/services东西放在哪里。Maven、Ant 等都有处理这种情况的方法。如果您在将这些文件添加到您的构建时遇到任何问题,我建议您询问有关您的特定构建过程的另一个问题。

于 2012-10-04T14:17:27.997 回答
1

如果你用下面的代码替换你的代码,它就可以工作。我怀疑这PropertiesLoader是在做不应该做的事情。

    Class<?> theClass;
    // Load the class.
    theClass = Class.forName("SpecificClass");
    // Create an instance of the class.
    C theInstance = (C) theClass.newInstance();


   BaseInterface base =  loadClass();//There is no problem in casting
于 2012-10-04T14:12:25.620 回答
0

Java 程序通常由系统类加载器加载。.properties 文件中引用的类由用户定义的类加载器加载。由不同类加载器加载的类被认为是不同的,即使它们具有相同的名称并且是从同一个类文件中加载的。在您的情况下,系统类加载器加载的接口 BaseInterface 与 PropertiesLoader 加载的 BaseInterface 不同。为了解决这个问题,PropertiesLoader 应该将 BaseInterface 的加载委托给系统类加载器。这样做的典型方法是使用系统类加载器作为 PropertiesLoader 的父类加载器。

于 2012-10-04T14:17:02.850 回答