7

2 つのインターフェイスを実装するクラスがいくつかあります。それらはすべて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 の Service Provider Interface (SPI) ライブラリを使用すると、クラスが実装するインターフェイスに基づいて、パラメーターなしのパブリック コンストラクターを持つクラスを動的にロードできます。これはすべて、META-INF/services.

まず、次のものが必要ですinterface

package com.example;

public interface SomeService {

    String getServiceId();

    String getDisplayName();
}

ServiceLoader次に、必要なときに、次を実装する Java のクラスを使用してそれらをロードできます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. このファイルには改行で区切られた実装のリストが含まれているため、実装例の場合、次の 1 行が含まれます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 に答える