私はJavaが初めてです。私の研究を通して、リフレクションはクラスとメソッドを呼び出すために使用され、どのメソッドが実装されているかどうかを知るために読んだ。
いつリフレクションを使用する必要がありますか?リフレクションの使用とオブジェクトのインスタンス化と従来の方法でのメソッドの呼び出しの違いは何ですか?
リフレクションは、プリコンパイルされたアドレスと定数を使用するだけでなく、バイトコードのメタデータを検査する必要があるため、名前でメソッドを呼び出すよりもはるかに遅くなります。
リフレクションもより強力です:protected
またはfinal
メンバーの定義を取得し、保護を削除して、変更可能と宣言されているかのように操作できます!これは明らかに、言語がプログラムに対して通常行う多くの保証を覆し、非常に非常に危険な場合があります。
そして、これはそれをいつ使うべきかをかなり説明しています。通常はしないでください。メソッドを呼び出したい場合は、それを呼び出します。メンバーを変更したい場合は、コンパイルの後ろに行くのではなく、変更可能であると宣言するだけです。
リフレクションの実用的な使用法の1つは、ユーザー定義のクラスと相互運用する必要があるフレームワークを作成する場合です。フレームワークの作成者は、メンバー(またはクラス)がどうなるかを知りません。リフレクションを使用すると、事前に知らなくてもクラスを処理できます。たとえば、リフレクションなしに複雑なアスペクト指向のライブラリを作成することは不可能だと思います。
別の例として、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
}
ただし、繰り返しになりますが、アルゴリズムで記述できる反復的なパターンがあります。
このアルゴリズムの入力はインターフェース定義です。
リフレクションでは、このアルゴリズムを使用して新しいクラスを定義できます。 Javaでは、Java.lang.reflect.Proxy
クラスのメソッドを使用してこれを行うことができます。さらに強力なライブラリがあります。
それでは、反射の欠点は何ですか?
Reflectionでは、プログラムの一部を自動的に同期させることができます。以前は、新しいインターフェイスを使用するためにプログラムを手動で更新する必要がありました。