Javaリフレクション-setAccessible(true)の影響


105

いくつかの注釈を使用して、クラスのフィールドの値を動的に設定しています。これをパブリック、保護、プライベートのいずれであるかに関係なく実行したいのでsetAccessible(true)set()メソッドを呼び出す前に毎回Fieldオブジェクトを呼び出しています。私の質問は、setAccessible()電話が現場にどのような影響を与えるかということです。

より具体的には、それがプライベートフィールドであり、この一連のコードがを呼び出しているとしますsetAccessible(true)。コードの他の場所がリフレクションを介して同じフィールドを取得することだった場合、フィールドはすでにアクセス可能ですか?または、getDeclaredFields()およびgetDeclaredField()メソッドは毎回Fieldオブジェクトの新しいインスタンスを返しますか?

質問を述べるもう1つの方法は、を呼び出した場合setAccessible(true)、完了後に元の値に戻すことがどのくらい重要かということです。

回答:


85

では、インスタンスsetAccessible()の動作を変更しますが、クラスの実際のフィールドは変更しません。ここだ文書(抜粋)は:AccessibleObjectField

の値はtrue、リフレクトされたオブジェクトが使用されるときにJava言語アクセス制御のチェックを抑制すべきであることを示します

そして実行可能な例:

public class FieldAccessible {
    public static class MyClass {
        private String theField;
    }

    public static void main(String[] args) throws Exception {
        MyClass myClass = new MyClass();
        Field field1 = myClass.getClass().getDeclaredField("theField");
        field1.setAccessible(true);
        System.out.println(field1.get(myClass)); // no exception
        Field field2 = myClass.getClass().getDeclaredField("theField");
        System.out.println(field2.get(myClass)); // IllegalAccessException
    }

}

@PhilipRegoインポート宣言を自分で書く必要があります。あなたがそれを行う方法を知っていることを願っています。
Moritz Petersen

問題が見つかりました。NoSuchFieldExceptionまたは親をスローまたは処理する必要があります。
Philip Rego

ええ、これは単なるサンプルコードです。つまり、throws Exceptionも処理しNoSuchFieldExceptionますが、より複雑な方法で処理することもできます。
Moritz Petersen

例外が発生します:Field field1 = myClass.getClass()。getDeclaredField( "theField"); だから、コンパイルすらしません、つまり、setAccessibleも関係ありませんか?
user2796104

32

getDeclaredFieldメソッドは、このオブジェクトが可変の持っている正確ので、新しいオブジェクトを毎回返すために持っているaccessible旗を。したがって、フラグをリセットする必要はありません。詳細については、このブログ投稿をご覧ください。


3

他のポスターが示しているように、setAccessibleはのそのインスタンスにのみ適用できるjava.lang.reflect.Fieldため、アクセシビリティを元の状態に戻す必要はありません。

しかしながら...

呼び出しをfield.setAccessible(true)永続的にする場合は、java.lang.Classおよびで基礎となるメソッドを使用する必要がありjava.lang.reflect.Fieldます。公開されているメソッドはインスタンスのコピーを送信するFieldので、次のようなことをするたびに「忘れてしまいます」class.getField(name)

import java.lang.reflect.*;
import sun.reflect.FieldAccessor;

public class Reflect {
    private static Method privateGetDeclaredFields;
    private static Method getFieldAccessor;

    public static Field[] fields(Class<?> clazz) throws Exception {
        return (Field[]) privateGetDeclaredFields.invoke(clazz, false);
    }

    public static <T> T get(Object instance, Field field) throws Exception {
        return ((FieldAccessor) getFieldAccessor.invoke(field, instance)).get(instance);
    }

    public static void set(Object instance, Field field, Object value) throws Exception {
        ((FieldAccessor) getFieldAccessor.invoke(field, instance)).set(instance, value);
    }

    static {
        try {
            // These are used to access the direct Field instances instead of the copies you normally get through #getDeclaredFields.
            privateGetDeclaredFields = Class.class.getDeclaredMethod("privateGetDeclaredFields", boolean.class);
            privateGetDeclaredFields.setAccessible(true);
            getFieldAccessor = Field.class.getDeclaredMethod("getFieldAccessor", Object.class);
            getFieldAccessor.setAccessible(true);
        } catch (Exception e) {
            // Should only occur if the internals change.
            e.printStackTrace();
        }
    }
}

更新:この実装はJava 8用であり、将来のバージョンではバックエンドを変更してこれを壊します。同じ戦略が依然として適用されますが、この戦略を継続したい場合は、


-1
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class PrivateVariableAcc {

    public static void main(String[] args) throws Exception {
        PrivateVarTest myClass = new PrivateVarTest();
        Field field1 = myClass.getClass().getDeclaredField("a");
        field1.setAccessible(true);
        System.out.println("This is access the private field-"
            + field1.get(myClass));
        Method mm = myClass.getClass().getDeclaredMethod("getA");
        mm.setAccessible(true);
        System.out.println("This is calling the private method-"
            + mm.invoke(myClass, null));
    }

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.