リフレクションは、それ自体、そして本質的に遅いものです。詳細については、この質問を参照してください。これにはいくつかの理由があります。ジョン・スキートはそれをうまく説明しています:
パラメーターなしのコンストラクターがあることを確認する パラメーターなしのコンストラクターのアクセス可能性を確認する 呼び出し元がリフレクションを使用するためのアクセス権を持っていることを確認する (実行時に) 割り当てられる必要があるスペースの量を計算する コンストラクター コードを呼び出す (それがわからないため)事前にコンストラクターが空であること)
基本的に、リフレクションは呼び出しの前に上記のすべてのステップを実行する必要がありますが、通常のメソッド呼び出しははるかに少ないことを行う必要があります。
B をインスタンス化するための JITted コードは非常に軽量です。基本的に、十分なメモリを割り当てる必要があります (GC が必要でない限り、ポインタをインクリメントするだけです)。それだけです。実際に呼び出すコンストラクタ コードはありません。JITがそれをスキップするかどうかはわかりませんが、どちらにしてもやることはあまりありません。
そうは言っても、Java が必要なことを行うのに十分なほど動的ではない場合が多くあり、リフレクションはシンプルでクリーンな代替手段を提供します。次のシナリオを検討してください。
Car
、Boat
、 など、さまざまなアイテムを表すクラスが多数ありますHouse
。
- どちらも同じクラスを拡張/実装します:
LifeItem
.
- ユーザーは、"Car"、"Boat"、または "House" の 3 つの文字列のいずれかを入力します。
- 目標は、パラメーターに基づいて LifeItem のメソッドにアクセスすることです。
- 頭に浮かぶ最初のアプローチは、if/else 構造を構築し、want を構築すること
LifeItem
です。ただし、これはあまりスケーラブルではなく、数十のLifeItem
実装があると非常に面倒になる可能性があります。
ここでリフレクションが役立ちます。LifeItem
名前に基づいてオブジェクトを動的に構築するために使用できるため、「Car」入力がCar
コンストラクターにディスパッチされます。突然、数百行の if/else コードだった可能性があるものが、単純な反射行に変わります。後者のシナリオは、文字列を使用した switch ステートメントが導入されているため、Java 7 以降のプラットフォームでは有効ではありませんが、それでも何百ものケースを含む switch は避けたいものです。ほとんどの場合、清潔さの違いは次のようになります。
反射なし:
public static void main(String[] args) {
String input = args[0];
if(input.equals("Car"))
doSomething(new Car(args[1]));
else if(input.equals("Boat"))
doSomething(new Boat(args[1]));
else if (input.equals("House"))
doSomething(new House(args[1]));
... // Possibly dozens more if/else statements
}
一方、リフレクションを利用すると、次のようになります。
public static void main(String[] args) {
String input = args[0];
try {
doSomething((LifeItem)Class.forName(input).getConstructor(String.class).newInstance(args[1]));
} catch (Exception ie) {
System.err.println("Invalid input: " + input);
}
}
個人的には、後者の方が前者よりもすっきりしていて、簡潔で、保守しやすいと思います。最終的には個人的な好みですが、これはリフレクションが役立つ多くのケースの 1 つにすぎません。
さらに、リフレクションを使用する場合は、できるだけ多くの情報をキャッシュするようにしてください。言い換えれば、単純で論理的なことを採用get(Declared)Method
します。たとえば、できる限りどこでも呼び出すのではなく、変数に格納して、使用するたびに参照を再取得するオーバーヘッドがないようにします。
したがって、これらは反射の長所と短所の両極端です。要約すると、リフレクションによってコードの可読性が向上する場合 (提示されたシナリオのように)、ぜひ試してみてください。その場合は、リフレクション コールの数を減らすことget*
を検討してください。リフレクション コールは最も簡単に削除できます。