私はJavaが初めてです。私の研究を通じて、クラスとメソッドを呼び出し、どのメソッドが実装されているかを知るためにリフレクションが使用されることを読みました。
いつリフレクションを使用する必要がありますか?リフレクションを使用してオブジェクトをインスタンス化し、従来の方法でメソッドを呼び出すことの違いは何ですか?
私はJavaが初めてです。私の研究を通じて、クラスとメソッドを呼び出し、どのメソッドが実装されているかを知るためにリフレクションが使用されることを読みました。
いつリフレクションを使用する必要がありますか?リフレクションを使用してオブジェクトをインスタンス化し、従来の方法でメソッドを呼び出すことの違いは何ですか?
回答:
リフレクションは、プリコンパイルされたアドレスと定数を使用する代わりに、バイトコード内のメタデータを検査する必要があるため、名前でメソッドを呼び出すよりもはるかに遅くなります。
また、リフレクションはより強力です。protected
つまり、final
メンバーの定義を取得し、保護を削除して、変更可能と宣言されているかのように操作できます。これは明らかに、言語がプログラムに対して通常行う多くの保証を覆すものであり、非常に非常に危険です。
そして、これはいつそれを使うべきかをかなり説明します。通常、しないでください。メソッドを呼び出したい場合は、呼び出してください。メンバーを変更したい場合は、コンパイルの後ろに行くのではなく、変更可能と宣言してください。
リフレクションの実用的な使用法の1つは、ユーザー定義のクラスと相互運用する必要があるフレームワークを作成する場合です。フレームワークの作成者は、メンバー(またはクラス)が何であるかを知りません。Reflectionを使用すると、事前に知らずにクラスを処理できます。たとえば、複雑なアスペクト指向のライブラリをリフレクションなしで書くことは不可能だと思います。
別の例として、JUnitは些細なリフレクションを使用していました。クラス内のすべてのメソッドを列挙し、呼び出されたtestXXX
ものはすべてテストメソッドであると想定し、それらのみを実行します。しかし、これは代わりに注釈を使用することで改善できるようになり、実際、JUnit 4は主に注釈に移動しました。
私はかつてあなたのようだった、私は反射についてあまり知らなかった-まだ知らない-しかし、私は一度それを使った。
2つの内部クラスを持つクラスがあり、各クラスには多くのメソッドがありました。
内部クラスのすべてのメソッドを呼び出す必要があり、それらを手動で呼び出すのは大変な作業でした。
リフレクションを使用すると、メソッド自体の数ではなく、2〜3行のコードでこれらすべてのメソッドを呼び出すことができます。
リフレクションの使用を3つのグループにグループ化します。
リフレクションを使用すると、存在しない可能性のあるコードをプログラムで処理し、信頼できる方法で実行できます。
「通常のコード」にはURLConnection c = null
、クラスローダーがこのクラスのロードの一部としてURLConnectionクラスをロードし、ClassNotFound例外をスローして終了するようなスニペットがあります。
リフレクションを使用すると、文字列形式の名前に基づいてクラスをロードし、それらに依存する実際のクラスを起動する前に、さまざまなプロパティ(制御外の複数のバージョンに有用)をテストできます。典型的な例は、JavaプログラムをOS Xでネイティブに見せるために使用されるOS X固有のコードであり、他のプラットフォームにはありません。
基本的に、リフレクションとは、プログラムのコードをデータとして使用することを意味します。
したがって、プログラムのコードが有用なデータソースである場合、リフレクションを使用することをお勧めします。(ただし、トレードオフがあるため、常に良いアイデアとは限りません。)
たとえば、単純なクラスを考えてみましょう。
public class Foo {
public int value;
public string anotherValue;
}
そしてそこからXMLを生成したい。XMLを生成するコードを書くことができます。
public XmlNode generateXml(Foo foo) {
XmlElement root = new XmlElement("Foo");
XmlElement valueElement = new XmlElement("value");
valueElement.add(new XmlText(Integer.toString(foo.value)));
root.add(valueElement);
XmlElement anotherValueElement = new XmlElement("anotherValue");
anotherValueElement.add(new XmlText(foo.anotherValue));
root.add(anotherValueElement);
return root;
}
しかし、これは大量の定型コードであり、クラスを変更するたびにコードを更新する必要があります。本当に、このコードが何をするのかを説明できます
これはアルゴリズムであり、アルゴリズムの入力はクラスです。その名前と、プロパティの名前、型、値が必要です。これがリフレクションの出番です。この情報にアクセスできます。Javaでは、Class
クラスのメソッドを使用して型を検査できます。
さらにいくつかのユースケース:
ただし、完全なリフレクションとは、既存のコード(それ自体が「イントロスペクション」と呼ばれる)を調べるだけでなく、コードを変更または生成することも意味します。Javaには、プロキシとモックという2つの顕著なユースケースがあります。
インターフェースがあるとしましょう:
public interface Froobnicator {
void froobnicateFruits(List<Fruit> fruits);
void froobnicateFuel(Fuel fuel);
// lots of other things to froobnicate
}
そして、あなたは何か面白いことをする実装を持っています:
public class PowerFroobnicator implements Froobnicator {
// awesome implementations
}
実際、2番目の実装もあります。
public class EnergySaverFroobnicator implements Froobnicator {
// efficient implementations
}
ログ出力も必要になりました。メソッドが呼び出されるたびにログメッセージが必要になります。ログ出力をすべてのメソッドに明示的に追加することもできますが、それは面倒であり、2回行う必要があります。実装ごとに1回。(実装を追加するとさらに多くなります。)
代わりに、プロキシを作成できます。
public class LoggingFroobnicator implements Froobnicator {
private Logger logger;
private Froobnicator inner;
// constructor that sets those two
public void froobnicateFruits(List<Fruit> fruits) {
logger.logDebug("froobnicateFruits called");
inner.froobnicateFruits(fruits);
}
public void froobnicateFuel(Fuel fuel) {
logger.logDebug("froobnicateFuel( called");
inner.froobnicateFuel(fuel);
}
// lots of other things to froobnicate
}
繰り返しますが、アルゴリズムで説明できる繰り返しパターンがあります。
このアルゴリズムの入力はインターフェイス定義です。
Reflectionでは、このアルゴリズムを使用して新しいクラスを定義できます。Javaでは、java.lang.reflect.Proxy
クラスのメソッドを使用してこれを行うことができ、さらに強力なライブラリがあります。
では、反射のマイナス面は何ですか?