Отражение Java - влияние setAccessible (true)

106

Я использую некоторые аннотации для динамической установки значений полей в классах. Поскольку я хочу делать это независимо от того, является ли он общедоступным, защищенным или частным, я setAccessible(true)вызываю объект Field каждый раз перед вызовом set()метода. У меня вопрос: какое влияние этот setAccessible()звонок оказывает на само поле?

Более конкретно, скажем, что это частное поле и этот набор кодовых вызовов setAccessible(true). Если бы какое-то другое место в коде предназначалось для получения того же поля посредством отражения, было бы это поле уже доступно? Или методы getDeclaredFields()и getDeclaredField()каждый раз возвращают новые экземпляры объекта Field?

Я предполагаю, что другой способ сформулировать вопрос: если я позвоню setAccessible(true), насколько важно вернуть его к исходному значению после того, как я закончу?

dnc253
источник

Ответы:

85

С setAccessible()его помощью вы изменяете поведение AccessibleObject, т.е. Fieldэкземпляр, но не фактическое поле класса. Вот документация (отрывок):

Значение 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, вам нужно написать декларации импорта самостоятельно. Я надеюсь, что вы знаете, как это сделать.
Мориц Петерсен
Обнаружена проблема. Вы должны выбросить или обработать NoSuchFieldException или parent.
Филип Рего
Да, это всего лишь образец кода. Я имею в виду, throws Exceptionтакже обрабатывает NoSuchFieldException, но вы можете обработать это более сложным способом.
Мориц Петерсен
Я получаю исключение в: Поле 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, будущие версии изменят бэкэнд, который нарушит это. Та же концепция по-прежнему применяется, если вы действительно хотите продолжить эту стратегию.

Col-E
источник
-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));
    }

}
РамЧандра Бхакар
источник