532

サードパーティのクラスの設計が不十分で、そのプライベートフィールドJARの1つにアクセスする必要があります。たとえば、なぜプライベートフィールドを選択する必要があるのですか?

class IWasDesignedPoorly {
    private Hashtable stuffIWant;
}

IWasDesignedPoorly obj = ...;

リフレクションを使用しての値を取得するにはどうすればよいstuffIWantですか?

4

15 に答える 15

719

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

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

編集aperkinsによってコメントされているように、フィールドへのアクセス、アクセス可能としての設定、および値の取得の両方でsがスローされる可能性がありますが、注意する必要がExceptionある唯一のチェック済み例外は上記でコメントされています。

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

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

IllegalAccessExceptionフィールドにアクセスできない場合(たとえば、フィールドがプライベートで、行を見逃してアクセスできるようになっていない場合)にスローされますf.setAccessible(true)

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

f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
于 2009-07-28T19:22:12.453 に答える
182

FieldUtilsapache commons-lang3から試してください:

FieldUtils.readField(object, fieldName, true);
于 2014-04-25T07:40:48.510 に答える
28

問題を解決する方法はリフレクションだけではありません(クラス/コンポーネントのプライベート機能/動作にアクセスすることです)

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

以下で説明するように、これにより、単にフィールドにアクセス/変更するのではなく、プライベート状態にアクセス/変更するというより広い問題が解決されます。

.jarもちろん、これには署名しないことが必要です。

于 2009-07-28T21:16:37.883 に答える
18

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

def obj = new IWasDesignedPoorly()
def hashTable = obj.getStuffIWant()
于 2010-01-19T14:48:16.423 に答える
10

JavaでReflectionを使用すると、あるクラスのすべてのprivate/publicフィールドとメソッドに別のクラスにアクセスできます。ただし、 Oracleの ドキュメント によると、セクションの欠点として、次のことが推奨されています。

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

これは、 Reflectionの基本的な概念を示すための次のコードスナップです。

Reflection1.java

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

}

それがお役に立てば幸いです。

于 2014-09-24T06:54:12.597 に答える
5

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

とは言うものの、このクラスの設計が非常に悪いためにそのようなハッカーに頼る場合は、別の方法を探す必要があります。確かに、この小さなハックで数時間節約できるかもしれませんが、将来的にはどれくらいの費用がかかりますか?

于 2009-07-28T19:36:58.430 に答える
4

Soot Java最適化フレームワークを使用して、バイトコードを直接変更します。 http://www.sable.mcgill.ca/soot/

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

于 2010-01-19T14:42:51.860 に答える
4

Springを使用する場合:

テストのコンテキストでは、ReflectionTestUtilsは、最小限の労力でここで役立ついくつかの便利なツールを提供します。「ユニットおよび統合テストのシナリオで使用するため」と説明されています。

テスト以外のコンテキストでは、 ReflectionUtilsという名前の同様のクラスもありますが、これは「内部使用のみを目的としています」と説明されています。これが何を意味するかについては、この回答を参照してください。

元の投稿の例に対処するには:

Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");
于 2017-10-23T13:25:20.667 に答える
3

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

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);
}
于 2017-02-13T09:08:11.500 に答える
2

そのためにjOORを使用できます。

class Foo {
    private final String value = "ABC";
}
class Bar {
    private final Foo foo = new Foo();
    public String value() {
        return org.joor.Reflect
            .on(this.foo)
            .field("value")
            .get();
    }
}
class BarTest {
    @Test
    void accessPrivateField() {
        Assertions.assertEquals(new Bar().value(), "ABC");
    }
}
于 2020-07-01T22:22:29.183 に答える
2

Java9では変数ハンドルが導入されました。それらを使用して、クラスのプライベートフィールドにアクセスできます。

この例のコードは次のようになります。

var lookup = MethodHandles.lookup();
var handle = MethodHandles
    .privateLookupIn(IWasDesignedPoorly.class, lookup)
    .findVarHandle(IWasDesignedPoorly.class, "stuffIWant", Hashtable.class);
var value = handle.get(obj);

またLookupVarHandleオブジェクトをstatic finalフィールドとして使用することをお勧めします。

于 2021-12-01T15:25:37.637 に答える
1

リフレクションに関する追加の注意:いくつかの特殊なケースで、同じ名前の複数のクラスが異なるパッケージに存在する場合、一番上の回答で使用されているリフレクションがオブジェクトから正しいクラスを選択できない可能性があることを確認しました。したがって、オブジェクトの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);

(これは私のために機能していなかったサンプルクラスです)

于 2015-09-09T07:59:42.433 に答える
1

Manifoldの @JailBreakを使用して、タイプセーフなJavaリフレクションを直接実行できます。

@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は、内部で効率的なリフレクションコードを生成します。

マニホールドの詳細をご覧ください。

于 2018-12-09T04:22:33.967 に答える
1

XrayInterfaceツールを使用すると非常に簡単です。不足しているゲッター/セッターを定義するだけです。

interface BetterDesigned {
  Hashtable getStuffIWant(); //is mapped by convention to stuffIWant
}

設計が不十分なプロジェクトをX線撮影します。

IWasDesignedPoorly obj = new IWasDesignedPoorly();
BetterDesigned better = ...;
System.out.println(better.getStuffIWant());

内部的には、これは反射に依存しています。

于 2019-11-27T12:39:53.563 に答える
-2

ケースの問題を回避してみてください。データを設定/取得するカラスは、独自のクラスの1つです。

public setter(Field f, Object value)とを作成するだけpublic Object getter(Field f)です。これらのメンバー関数内で、独自にセキュリティチェックを行うこともできます。セッターの例:

class myClassName {
    private String aString;

    public set(Field field, Object value) {
        // (A) do some checkings here  for security

        // (B) set the value
        field.set(this, value);
    }
}

もちろん、フィールド値を設定する前にjava.lang.reflect.Field、を確認する必要があります。sString

私はこの手法を一般的なResultSet-to-and-from-model-mapperで使用します。

于 2019-11-25T15:05:29.767 に答える