Відбиття Java - вплив setAccessible (true)


105

Я використовую деякі примітки для динамічного встановлення значень полів у класах. Оскільки я хочу це зробити незалежно від того, є він публічним, захищеним чи приватним, я setAccessible(true)кожен раз звертаюся до об'єкта Field перед тим, як викликати set()метод. Моє запитання - який вплив має setAccessible()виклик на саме поле?

Більш конкретно, скажімо, що це приватне поле і цей набір кодових викликів setAccessible(true). Якби тоді якесь інше місце в коді було знайти те саме поле за допомогою відображення, чи було б поле вже доступним? Або методи getDeclaredFields()і getDeclaredField()повертають нові екземпляри об'єкта Field кожен раз?

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

Відповіді:


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 або батьків.
Філіп Рего

Так, це лише зразок коду. Я маю на увазі throws Exceptionтакож ручки NoSuchFieldException, але ви, можливо, захочете впоратися з цим більш детально.
Моріц Петерсен

Я отримую Виняток на: Field field1 = myClass.getClass (). GetDeclaredField ("theField"); тому він навіть не компілюється, тобто setAccessible навіть не має значення?
користувач2796104

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

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.