143

I'm using some annotations to dynamically set values of fields in classes. Since I want to do this regardless of whether it's public, protected, or private, I am a calling setAccessible(true) on the Field object every time before calling the set() method. My question is what kind of impact does the setAccessible() call have on the field itself?

More specifically, say it is a private field and this set of code calls setAccessible(true). If some other place in the code was then to retrieve the same field through reflection, would the field already be accessible? Or does the getDeclaredFields() and getDeclaredField() methods return new instances of a Field object each time?

I guess another way of stating the question is if I call setAccessible(true), how important is it to set it back to the original value after I'm done?

dnc253
  • 39,967
  • 41
  • 141
  • 157

4 Answers4

129

With setAccessible() you change the behavior of the AccessibleObject, i.e. the Field instance, but not the actual field of the class. Here's the documentation (excerpt):

A value of true indicates that the reflected object should suppress checks for Java language access control when it is used

And a runnable example:

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
    }

}
jzheaux
  • 7,042
  • 3
  • 22
  • 36
Moritz Petersen
  • 12,902
  • 3
  • 38
  • 45
  • @PhilipRego you need to write the import declarations yourself. I hope that you know how to do that. – Moritz Petersen Jan 23 '19 at 13:13
  • Found issue. You have to throw or handle NoSuchFieldException or parent. – Philip Rego Jan 23 '19 at 15:03
  • 2
    Yeah, this is just sample code. I mean, `throws Exception` also handles `NoSuchFieldException`, but you may want to handle it in a more elaborate way. – Moritz Petersen Jan 24 '19 at 16:36
  • I am getting the Exception on the: Field field1 = myClass.getClass().getDeclaredField("theField"); so it doesn't even compile, i.e. setAccessible won't even matter? – Hmbucker Sep 26 '19 at 19:47
44

The getDeclaredField method has to return a new object each time, exactly because this object has the mutable accessible flag. So there is no need to reset the flag. You can find the full details in this blog post.

Jörn Horstmann
  • 33,639
  • 11
  • 75
  • 118
8

As other posters have indicated, setAccessible is only applicable to that instance of your java.lang.reflect.Field, so setting the accessibility back to its original state is not needed.

However...

If you want your calls to field.setAccessible(true) to be persistent you need to use underlying methods in java.lang.Class and java.lang.reflect.Field. The public facing methods send you copies of the Field instance, so it "forgets" after each time you do something like 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();
        }
    }
}

Update: This implementation is for Java 8, future versions change the backend which breaks this. The same concept still applies though should you really wish to continue this strategy.

Col-E
  • 155
  • 3
  • 10
-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));
    }

}
Bombe
  • 81,643
  • 20
  • 123
  • 127