web-dev-qa-db-ja.com

Javaで別のクラスからプレイフィールドフィールドの値を読み取る方法

私はサードパーティのJARでデザインが良くないクラスを持っています、そして私はそのプライベートフィールドの一つにアクセスする必要があります。例えば、なぜ私がプライベートフィールドを選択する必要があるのですか?

class IWasDesignedPoorly {
    private Hashtable stuffIWant;
}

IWasDesignedPoorly obj = ...;

stuffIWantの値を得るためにどうやってリフレクションを使うことができますか?

447
Frank Krueger

プライベートフィールドにアクセスするには、それらをクラスの宣言済みフィールドから取得してからアクセス可能にする必要があります。

Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException
f.setAccessible(true);
Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException

EDITaperkinsでコメントされているとおり、両方ともフィールドにアクセスし、アクセス可能として設定ただし、注意が必要な唯一のチェック済みの例外は上記のとおりですが、値を取得するとすべてExceptionsがスローされます。

宣言されたフィールドに対応しない名前でフィールドを要求した場合、NoSuchFieldExceptionがスローされます。

obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException

フィールドがアクセスできない場合(たとえば、フィールドが非公開で、f.setAccessible(true)行を省略してアクセスできない場合など)、IllegalAccessExceptionがスローされます。

スローされる可能性のあるRuntimeExceptionsは、SecurityExceptions(JVMのSecurityManagerでフィールドのアクセシビリティを変更できない場合)、またはフィールドのクラスの型ではないオブジェクトでフィールドにアクセスしようとした場合はIllegalArgumentExceptionsのいずれかです。

f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
613
oxbow_lakes

Apache commons-lang3から FieldUtils を試してください。

FieldUtils.readField(object, fieldName, true);
137
yegor256

あなたの問題を解決する唯一の方法はリフレクションではありません(それはプライベートな機能/クラス/コンポーネントの振る舞いにアクセスすることです)。

別の解決策は、.jarからクラスを抽出し、(say) Jode または Jad を使って逆コンパイルし、フィールドを変更することです。 (またはアクセサを追加して)元の.jarに対して再コンパイルします。次に、新しい.classをクラスパスの.jarの前に置くか、またはそれを.jarに再挿入します。 (jarユーティリティを使用すると、既存の.jarを抽出して再挿入できます)

後述するように、これは単にフィールドにアクセスしたり変更したりするのではなく、プライベート状態にアクセスし変更するより広い問題を解決します。

これはもちろん.jarが署名されていないことを必要とします。

24
Brian Agnew

まだ言及されていないもう1つのオプション:useGroovy。 Groovyでは、言語設計の副作用としてプライベートインスタンス変数にアクセスできます。あなたがフィールドのゲッターを持っているかどうかに関係なく、あなたはただ使うことができます

def obj = new IWasDesignedPoorly()
def hashTable = obj.getStuffIWant()
16
lucas

JavaでのReflectionを使用すると、あるクラスのすべてのprivate/publicフィールドおよびメソッドに別のクラスにアクセスできます。Oracle/に従って documentation セクションの欠点彼らはそれをお勧めします:

"リフレクションを使用すると、プライベートフィールドやメソッドへのアクセスなど、リフレクティブではないコードでは無効な操作をコードで実行できるため、リフレクションを使用すると予期しない副作用が発生し、反射的なコードは抽象化を破るので、プラットフォームのアップグレードによって動作が変わる可能性があります。」

以下は、Reflectionの基本概念を示すためのコードの抜粋です。

リフレクション1

public class Reflection1{

    private int i = 10;

    public void methoda()
    {

        System.out.println("method1");
    }
    public void methodb()
    {

        System.out.println("method2");
    }
    public void methodc()
    {

        System.out.println("method3");
    }

}

Reflection2.Java

import Java.lang.reflect.Field;
import Java.lang.reflect.InvocationTargetException;
import Java.lang.reflect.Method;


public class Reflection2{

    public static void main(String ar[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
    {
        Method[] mthd = Reflection1.class.getMethods(); // for axis the methods 

        Field[] fld = Reflection1.class.getDeclaredFields();  // for axis the fields  

        // Loop for get all the methods in class
        for(Method mthd1:mthd)
        {

            System.out.println("method :"+mthd1.getName());
            System.out.println("parametes :"+mthd1.getReturnType());
        }

        // Loop for get all the Field in class
        for(Field fld1:fld)
        {
            fld1.setAccessible(true);
            System.out.println("field :"+fld1.getName());
            System.out.println("type :"+fld1.getType());
            System.out.println("value :"+fld1.getInt(new Reflaction1()));
        }
    }

}

それが役立つことを願っています。

8
Simmant

Oxbow_lakesが述べているように、リフレクションを使用してアクセス制限を回避することができます(SecurityManagerが許可してくれると仮定して)。

とは言っても、このクラスがあまりにもひどく設計されているためにこのようなハッカーに頼るのであれば、代替手段を探すべきです。このちょっとしたハックで、今は数時間節約できるかもしれませんが、それはいくらかかりますか。

5

バイトコードを直接変更するには、Soot Java Optimizationフレームワークを使用してください。 http://www.sable.mcgill.ca/soot/

Sootは完全にJavaで書かれており、新しいJavaバージョンで動作します。

4
pcpratts

次のことを行う必要があります。

private static Field getField(Class<?> cls, String fieldName) {
    for (Class<?> c = cls; c != null; c = c.getSuperclass()) {
        try {
            final Field field = c.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field;
        } catch (final NoSuchFieldException e) {
            // Try parent
        } catch (Exception e) {
            throw new IllegalArgumentException(
                    "Cannot access field " + cls.getName() + "." + fieldName, e);
        }
    }
    throw new IllegalArgumentException(
            "Cannot find field " + cls.getName() + "." + fieldName);
}
2
Luke Hutchison

リフレクションについてのもう一つの注意:私はいくつかの特別な場合に、同じ名前を持ついくつかのクラスが異なるパッケージに存在するとき、トップアンサーで使用されるリフレクションがオブジェクトから正しいクラスを選択できないかもしれないことを観察しました。そのため、オブジェクトのpackage.classが何であるかがわかっている場合は、次のようにプライベートフィールドの値にアクセスすることをお勧めします。

org.deeplearning4j.nn.layers.BaseOutputLayer ll = (org.deeplearning4j.nn.layers.BaseOutputLayer) model.getLayer(0);
Field f = Class.forName("org.deeplearning4j.nn.layers.BaseOutputLayer").getDeclaredField("solver");
f.setAccessible(true);
Solver s = (Solver) f.get(ll);

(これは私のために働いていなかった例のクラスです)

1
xtof54

直接の型保証されたJavaリフレクションには、 Manifoldの@ JailBreakを使用できます。

@JailBreak Foo foo = new Foo();
foo.stuffIWant = "123;

public class Foo {
    private String stuffIWant;
}

@JailBreakは、fooの階層内のすべてのメンバーに直接アクセスできるように、コンパイラ内のFooローカル変数をロック解除します。

同様に、jailbreak()拡張メソッドを1回限りの使用に使用できます。

foo.jailbreak().stuffIWant = "123";

jailbreak()メソッドを通して、あなたはFooの階層構造のどのメンバーにもアクセスできます。

どちらの場合も、パブリックフィールドの場合と同様に、コンパイラはフィールドアクセスを型安全に解決します。一方、Manifoldは、フードの下で効率的なリフレクションコードを生成します。

マニホールド についてもっと詳しく知る。

0
Scott

Springを使用している場合、 ReflectionTestUtils は最小限の労力でここで役立ついくつかの便利なツールを提供します。これは「単体テストおよび統合テストのシナリオで使用するためのもの」として記述されています。 ReflectionUtils という名前の類似クラスもありますが、これは「内部使用のみを目的としています」と記述されています。 - これが何を意味するのかについての解釈については この答え を見てください。

投稿された例に対処するには

Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");
0
Steve Chambers