9

ごく最近、リフレクション API に出会いました。驚いたことに、プライベート変数にアクセスして変更することもできます。次のコードを試しました

import java.lang.reflect.Field;

public class SomeClass{
    private String name = "John";
}

public class Test{
    public static void main(String args[]) throws Exception {
        SomeClass myClass = new SomeClass();

        Field fs = myClass.getClass().getDeclaredField("name");
        fs.setAccessible(true);

        System.out.println("Variable is " + fs.getName() + " and value is "
                + fs.get(myClass));

        fs.set(myClass, "Sam");
        System.out.println("Variable is " + fs.getName() + " and value is "
                + fs.get(myClass));
    }
}

そして、次の出力を得ました。

Variable is name and value is John
Variable is name and value is Sam

Java はオブジェクト指向言語であり、その主な機能はデータ カプセル化、継承、ポリモーフィズムなどです。リフレクション API は、データ カプセル化の目的そのものを変えていませんか? Reflection API を使用する必要があるのはなぜですか? テスト目的で使用できるサイトをいくつか読みましたが、モジュールはテストされており、JUnitテストケースを使用して簡単に実行できます。では、なぜこのようなハックがあるのか​​、誰か説明できますか?

4

4 に答える 4

12

リフレクション API は、データ カプセル化の目的そのものを変えていませんか?

はいといいえ。

  • はい、リフレクション API を使用すると、データのカプセル化が壊れる可能性があります。
  • いいえ、リフレクション API のすべての使用がデータのカプセル化を破るわけではありません。実際、賢明なプログラマーは、そうする正当な理由がある場合にのみ、リフレクション API を介してカプセル化を解除します。
  • いいえ、リフレクション API はデータのカプセル化の目的を変更しません。データのカプセル化の目的は同じままです...誰かが故意にそれを破ったとしても。

Reflection API を使用する必要があるのはなぜですか?

カプセル化を壊さないリフレクションの用途はたくさんあります。たとえば、リフレクションを使用して、クラスに含まれるスーパー タイプ、含まれる注釈、含まれるメンバーを調べ、アクセス可能なメソッドとコンストラクターを呼び出し、アクセス可能なフィールドの読み取りと更新などを行います。

また、カプセル化を破るさまざまな反射を使用することが (さまざまな程度で) 許容される状況があります。

  • 特定の単体テストを実装する最も簡単な方法 (または唯一の方法) として、カプセル化された型 (プライベート フィールドへのアクセス/変更など) の内部を調べる必要がある場合があります。

  • 依存性注入 (別名 IoC)、シリアライゼーション、永続化の一部の形式では、プライベート フィールドへのアクセスや更新が必要になります。

  • 非常にまれに、修正できないクラスのバグを回避するために、カプセル化を解除する必要があります。

テスト目的で使用できるサイトをいくつか読みましたが、モジュールはテストされており、JUnit テスト ケースを使用して簡単に実行できます。では、なぜこのようなハックがあるのか​​、誰か説明できますか?

これは、クラスの設計によって異なります。テスト可能になるように設計されたクラスは、「プライベート」状態にアクセスする必要なくテスト可能になるか、テストを可能にするためにその状態 (protectedゲッターなど) を公開します。クラスがこれを行わない場合、JUnit テストはリフレクションを使用して抽象化の内部を調べる必要がある場合があります。

これは望ましくありません (IMO) が、誰かが書いたクラスの単体テストを書いていて、テスト容易性を向上させるために API を「微調整」できない場合は、リフレクションを使用するか、まったくテストしないかを選択する必要があるかもしれません。 .


要するに、データのカプセル化は私たちが (Java で) 達成しようと努力している理想ですが、それを破るか無視することが実用的に正しい場合もあります。

すべての OO 言語が、Java のように強力なデータ カプセル化をサポートしているわけではないことに注意してください。たとえば、Python と Javascript はどちらも間違いなく OO 言語ですが、どちらもあるクラスが別のクラスのオブジェクトの状態にアクセスして変更することを容易にし、さらには他のクラスの動作を変更することさえできます。強力なデータ抽象化は、オブジェクト指向が何を意味するかについてのすべての人の見解の中心ではありません。

于 2013-05-19T13:34:38.160 に答える
0

リフレクション API は、データ カプセル化の目的そのものを変えていませんか?

ルールにとらわれすぎないでください。それらを壊すと便利な場合があります。

反射は非常に便利です。たとえば、検索結果の変更をログに記録して、データベースにログを記録できるようにするコードを考えてみましょう。

public static void log(String userID, Object bOld, Object bNew)
          throws IntrospectionException, IllegalAccessException, InvocationTargetException {
      String res = "";//String to hold the change record
      boolean changed = false;
      try {
        if(bOld != null){ //if this is an update
            BeanInfo beanInfo = Introspector.getBeanInfo(bOld.getClass());
            res = bOld.getClass().getSimpleName() + " - ";
            //loop and compare old values with new values and add them to our string if they are changed
            for (PropertyDescriptor prop : beanInfo.getPropertyDescriptors()) {
                Method getter = prop.getReadMethod();
                Object vOld = getter.invoke(bOld); //old value
                Object vNew = getter.invoke(bNew); //new value
                if (vOld == vNew || (vOld != null && vOld.equals(vNew))) {
                  continue;
                }
                changed = true;
                res = res + "(" + prop.getName()  + ", " +  vOld  + ", " + vNew + ")";
            }
          }

このようにする方がはるかに簡単です。ゲッターを使用していた場合、クラスごとに個別のメソッドを作成し、新しいフィールドが追加されるたびに変更する必要があります。リフレクションを使用すると、すべてのクラスを処理するメソッドを 1 つ作成するだけで済みます。ここで変更をログに記録する方法について書きます

于 2013-05-19T14:43:26.403 に答える
0

はい、オブジェクト指向の概念に違反しています。ただし、Java セキュリティ モデルが壊れることはありません。必要に応じて、Java セキュリティ マネージャで制御できます。Java リフレクション自体は便利なものです。これは、実行時にクラスを処理する機能を備えた非常に便利な概念である注釈と IOC で使用されます。

于 2013-05-19T13:26:10.150 に答える
0

と反射を混在Encapsulationさせることはできません。Polymorphism

リフレクションにはまったく異なる目的があります。リフレクションを使用すると、型を動的に作成して実行できます。実行時にクラスのメンバーに動的にアクセスできます。


たとえば、最近Plugins、アプリケーションで実行するためにリフレクションを使用していました。つまり、特定のディレクトリから dll をロードし、共有インターフェイスにキャストして、リフレクションを介してクラスのオブジェクトを作成しました。

于 2013-05-19T13:32:12.017 に答える