なぜリフレクションを使用する必要があるのですか?


29

私はJavaが初めてです。私の研究を通じて、クラスとメソッドを呼び出し、どのメソッドが実装されているかを知るためにリフレクションが使用されることを読みました。

いつリフレクションを使用する必要がありますか?リフレクションを使用してオブジェクトをインスタンス化し、従来の方法でメソッドを呼び出すことの違いは何ですか?



10
投稿する前に調査を共有してください。StackExchange(@Jalaynが述べたように)およびWebには、リフレクションに関する一般的な資料がたくさんあります。たとえば、Reflectionに関するJavaチュートリアルを読んで、さらに具体的な質問がある場合は、後で戻ってみることをお勧めします。
ペテルトレック

1
100万回のduが必要です。
DeadMG

3
少数のプロのプログラマーが「できる限りめったに、たぶん決して」と答えます。
ロスパターソン

回答:


38
  • リフレクションは、プリコンパイルされたアドレスと定数を使用する代わりに、バイトコード内のメタデータを検査する必要があるため、名前でメソッドを呼び出すよりもはるかに遅くなります。

  • また、リフレクションはより強力です。protectedつまり、finalメンバーの定義を取得し、保護削除して、変更可能と宣言されているかのように操作できます。これは明らかに、言語がプログラムに対して通常行う多くの保証を覆すものであり、非常に非常に危険です。

そして、これはいつそれを使うべきかをかなり説明します。通常、しないでください。メソッドを呼び出したい場合は、呼び出してください。メンバーを変更したい場合は、コンパイルの後ろに行くのではなく、変更可能と宣言してください。

リフレクションの実用的な使用法の1つは、ユーザー定義のクラスと相互運用する必要があるフレームワークを作成する場合です。フレームワークの作成者は、メンバー(またはクラス)が何であるかを知りません。Reflectionを使用すると、事前に知らずにクラスを処理できます。たとえば、複雑なアスペクト指向のライブラリをリフレクションなしで書くことは不可能だと思います。

別の例として、JUnitは些細なリフレクションを使用していました。クラス内のすべてのメソッドを列挙し、呼び出されたtestXXXものはすべてテストメソッドであると想定し、それらのみを実行します。しかし、これは代わりに注釈を使用することで改善できるようになり、実際、JUnit 4は主に注釈に移動しました。


6
「より強力な」には注意が必要です。チューリングの完全性を得るためにリフレクションは必要ないので、計算にリフレクションは必要ありません。もちろん、Turing completeは、I / O機能や反射など、他の種類の電力については何も言いません。
Steve314

1
反射は必ずしも「ずっと遅い」とは限りません。リフレクションを1回使用して、直接呼び出しラッパーバイトコードを生成できます。
SKロジック

2
必要なときにそれを知ることができます。なぜ(言語生成以外で)それが必要なのかとよく疑問に思いました。その後、突然、私は...他の開発者からパネルを取得して、保守しているシステムの1つにアクセスするときに、親/子チェーンを上下に移動してデータを覗き見しなければなりませんでした。
ブライアン

@ SK-logic:実際にバイトコードを生成するために、リフレクションはまったく必要ありません(実際、リフレクションにはバイトコード操作用のAPIはまったく含まれていません!)。
ヨアヒムザウアー

1
@JoachimSauerはもちろんですが、この生成されたバイトコードを読み込むにはリフレクションAPIが必要です。
SKロジック

15

私はかつてあなたのようだった、私は反射についてあまり知らなかった-まだ知らない-しかし、私は一度それを使った。

2つの内部クラスを持つクラスがあり、各クラスには多くのメソッドがありました。

内部クラスのすべてのメソッドを呼び出す必要があり、それらを手動で呼び出すのは大変な作業でした。

リフレクションを使用すると、メソッド自体の数ではなく、2〜3行のコードでこれらすべてのメソッドを呼び出すことができます。


4
なぜ下票なのですか?
マフムードホッサム

1
前文への賛成
ドミトリーミンコフスキー

1
@MahmoudHossamはおそらくベストプラクティスではありませんが、答えは展開できる重要な戦術を示しています。
ankush981

13

リフレクションの使用を3つのグループにグループ化します。

  1. 任意のクラスのインスタンス化。たとえば、依存性注入フレームワークでは、インターフェイスThingDoerがクラスNetworkThingDoerによって実装されることをおそらく宣言します。次に、フレームワークはNetworkThingDoerのコンストラクターを見つけてインスタンス化します。
  2. 他の形式へのマーシャリングとアンマーシャリング。たとえば、Beanの規則に従うゲッターと設定を使用してオブジェクトをJSONにマッピングし、再び元に戻します。コードは実際にはフィールドやメソッドの名前を知らず、クラスを調べます。
  3. クラスをリダイレクトの層にラップする(おそらくListは実際にはロードされず、データベースから取得する方法を知っているものへのポインター)か、クラスを完全に偽造する(jMockはインターフェイスを実装する合成クラスを作成します)テスト目的で)。

これは、StackExchangeで発見したリフレクションの最良の説明です。ほとんどの答えは、Java Trailの言葉(「これらのすべてのプロパティにアクセスできる」が、そうする理由ではない)を繰り返し、なしで行う方がはるかに簡単なリフレクションで物事を行う例を提供するか、Springそれを使用します。この回答では、実際に3つの有効な例を示していますが、これらはリフレクションなしでJVMによって簡単に回避することはできません。ありがとうございました!
ndm13

3

リフレクションを使用すると、存在しない可能性のあるコードをプログラムで処理し、信頼できる方法で実行できます。

「通常のコード」にはURLConnection c = null、クラスローダーがこのクラスのロードの一部としてURLConnectionクラスをロードし、ClassNotFound例外をスローして終了するようなスニペットがあります。

リフレクションを使用すると、文字列形式の名前に基づいてクラスをロードし、それらに依存する実際のクラスを起動する前に、さまざまなプロパティ(制御外の複数のバージョンに有用)をテストできます。典型的な例は、JavaプログラムをOS Xでネイティブに見せるために使用されるOS X固有のコードであり、他のプラットフォームにはありません。


2

基本的に、リフレクションとは、プログラムのコードをデータとして使用することを意味します。

したがって、プログラムのコードが有用なデータソースである場合、リフレクションを使用することをお勧めします。(ただし、トレードオフがあるため、常に良いアイデアとは限りません。)

たとえば、単純なクラスを考えてみましょう。

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;
}

しかし、これは大量の定型コードであり、クラスを変更するたびにコードを更新する必要があります。本当に、このコードが何をするのかを説明できます

  • クラスの名前でXML要素を作成します
  • クラスの各プロパティに対して
    • プロパティの名前でXML要素を作成します
    • プロパティの値をXML要素に入れます
    • XML要素をルートに追加します

これはアルゴリズムであり、アルゴリズムの入力はクラスです。その名前と、プロパティの名前、型、値が必要です。これがリフレクションの出番です。この情報にアクセスできます。Javaでは、Classクラスのメソッドを使用して型を検査できます。

さらにいくつかのユースケース:

  • クラスのメソッド名に基づいてWebサーバーでURLを定義し、メソッドの引数に基づいてURLパラメーターを定義する
  • クラスの構造をGraphQL型定義に変換します
  • ユニットテストケースとして名前が「test」で始まるクラスのすべてのメソッドを呼び出します

ただし、完全なリフレクションとは、既存のコード(それ自体が「イントロスペクション」と呼ばれる)を調べるだけでなく、コードを変更または生成することも意味します。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
}

繰り返しますが、アルゴリズムで説明できる繰り返しパターンがあります。

  • ロガープロキシは、インターフェイスを実装するクラスです
  • インターフェースとロガーの別の実装を取るコンストラクターがあります
  • インターフェイス内のすべてのメソッドに対して
    • 実装は「$ methodname called」というメッセージを記録します
    • そして、内部インターフェイスで同じメソッドを呼び出し、すべての引数を渡します

このアルゴリズムの入力はインターフェイス定義です。

Reflectionでは、このアルゴリズムを使用して新しいクラスを定義できます。Javaでは、java.lang.reflect.Proxyクラスのメソッドを使用してこれを行うことができ、さらに強力なライブラリがあります。

では、反射のマイナス面は何ですか?

  • コードが理解しにくくなります。あなたはコードの具体的な効果からさらに取り除かれた抽象化の一つのレベルです。
  • コードのデバッグが難しくなります。特にコード生成ライブラリの場合、実行されるコードは作成したコードではなく、生成したコードであり、デバッガーはそのコードを表示できない(またはブレークポイントを配置できる)場合があります。
  • コードが遅くなります。型情報を動的に読み取り、ハードコーディングアクセスの代わりにランタイムハンドルによってフィールドにアクセスするのは遅くなります。動的なコード生成は、デバッグがさらに困難になるという犠牲を払って、この影響を軽減できます。
  • コードがより脆弱になる場合があります。動的リフレクションアクセスはコンパイラによって型チェックされませんが、実行時にエラーをスローします。

1

Reflectionは、プログラムの一部を自動的に同期します。以前は、新しいインターフェイスを使用するには、プログラムを手動で更新する必要がありました。


5
この場合に支払う代償は、IDEでコンパイラによる型チェックとリファクタリングの安全性が失われることです。それはトレードオフです。
-Barend
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.