9

ここに示すように、いくつかのクラスがあります

public class TrueFalseQuestion implements Question{
    static{
        QuestionFactory.registerType("TrueFalse", "Question");
    }
    public TrueFalseQuestion(){}
}

...

public class QuestionFactory {

 static final HashMap<String, String > map =  new HashMap<String,String>();

 public static void registerType(String questionName, String ques ) {
     map.put(questionName, ques);
     }
 }



public class FactoryTester {
    public static void main(String[] args) {
        System.out.println(QuestionFactory.map.size());
        // This prints 0. I want it to print 1
    }
}

TrueFalseQuestionメイン メソッドを実行したときに 0 ではなく 1 を取得するように、静的メソッドが常に実行されるようにクラスを変更するにはどうすればよいですか? メインメソッドを変更したくありません。

サブクラスがファクトリに登録されるファクトリ パターンを実際に実装しようとしていますが、この質問のコードを簡略化しました。

4

4 に答える 4

6

あなたは呼び出すことができます:

Class.forName("yourpackage.TrueFalseQuestion");

これにより、実際に触れることなくクラスがロードされ、静的初期化ブロックが実行されます。

于 2010-04-06T05:50:26.237 に答える
5

クラスをファクトリに登録するTrueFalseQuestionには、静的初期化子を呼び出す必要があります。クラスの静的初期化子を実行するには、クラスを参照するか、または呼び出されるTrueFalseQuestion前にリフレクションによってロードする必要があります。QuestionFactory.map.size()メソッドをそのままにしておく場合は、参照するか、静的初期化子mainでリフレクションによってロードする必要があります。QuestionFactoryこれは良い考えだとは思いませんが、あなたの質問に答えるだけです:)それらを構築するためにQuestionFactory実装するすべてのクラスを知っていてもかまわない場合は、それらを直接参照するか、リフレクションを介してロードすることができます。Question何かのようなもの:

public class QuestionFactory {

 static final HashMap<String, String > map =  new HashMap<String,String>();

 static {
    this.getClassLoader().loadClass("TrueFalseQuestion");
    this.getClassLoader().loadClass("AnotherTypeOfQuestion"); // etc.
 }

 public static void registerType(String questionName, String ques ) {
     map.put(questionName, ques);
     }
 }

mapの宣言と構築がstaticブロックの前にあることを確認してください。QuestionFactoryの実装に関する知識が不要な場合Questionは、 によってロードされる構成ファイルにそれらをリストする必要がありますQuestionFactory。私が考えることができる唯一の他の(おそらく非常識な)方法は、実装するクラスのクラスパス全体を調べることです:)実装するすべてのクラスが同じパッケージに属する必要がQuestionある場合、それはよりうまくいくかもしれません-注:Question私はこの解決策を支持していません;)

静的イニシャライザでこれを行うとは思わない理由は、 のQuestionFactoryようなクラスがTrueFalseQuestionを呼び出す独自の静的イニシャライザを持っているためQuestionFactoryです。その時点では、それは不完全に構築されたオブジェクトであり、単にトラブルを求めているだけです。構成方法を知りたいクラスを単にリストした構成ファイルを用意し、QuestionFactoryそれらをコンストラクターに登録することは良い解決策ですが、mainメソッドを変更することを意味します。

于 2010-04-06T06:33:23.497 に答える
4

クラスがロードされていない場合、クラスの静的初期化子は実行できません。

そのため、すべての正しいクラスをロードする必要があります (コンパイル時にすべてを知っているわけではないため、これは困難です)、または静的初期化子の要件を取り除く必要があります。

後者を行う 1 つの方法は、ServiceLoader.

を使用するServiceLoaderと、ファイルを入れてMETA-INF/services/package.Questionすべての実装を一覧表示するだけです。.jar ファイルごとに 1 つずつ、このようなファイルを複数持つことができます。Questionこのようにして、メイン プログラムとは別に追加の実装を簡単に出荷できます。

では、次のように実装してQuestionFactory使用できるServiceLodaer.load(Question.class)を取得するために使用できます。ServiceLoaderIterable<Question>

for (Question q : ServiceLoader.load(Question.class)) {
    System.out.println(q);
}
于 2010-04-06T06:50:34.110 に答える
2

静的初期化子を実行するには、クラスをロードする必要があります。これを行うには、「メイン」クラスがクラスに (直接的または間接的に) 依存するか、直接的または間接的にクラスが動的に読み込まれるようにする必要があります。例えば使用Class.forName(...)

ソースコードに埋め込まれた依存関係を回避しようとしていると思います。そのため、静的な依存関係は受け入れられず、Class.forName(...)ハードコードされたクラス名を使用した への呼び出しも受け入れられません。

これにより、2 つの選択肢が残ります。

  • 一部のパッケージのリソース名を反復処理する厄介なコードを記述しClass.forName(...)、クラスのように見えるリソースをロードするために使用します。複雑なクラスパスがある場合、このアプローチはトリッキーであり、有効なクラスパスにリモート URL を持つ URLClassLoader が含まれている場合 (たとえば) は不可能です。

  • ロードするクラス名のリストを含むファイル (例: クラスローダー リソース) を作成し、ファイルを読み取ってClass.forName(...)各クラスをロードするために使用する簡単なコードを記述します。

于 2010-04-06T06:56:17.717 に答える