Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java reflection - impact of setAccessible(true)

People also ask

What is true about Java reflection?

In Java, reflection allows us to inspect and manipulate classes, interfaces, constructors, methods, and fields at run time. There is a class in Java named Class that keeps all the information about objects and classes at runtime. The object of Class can be used to perform reflection.

Is it good to use Java reflection?

Java Reflection is quite powerful and can be very useful. Java Reflection makes it possible to inspect classes, interfaces, fields and methods at runtime, without knowing the names of the classes, methods etc. at compile time.

What is setAccessible in Java?

setAccessible(boolean flag) method sets the accessible flag for this object to the indicated boolean value. A value of true indicates that the reflected object should suppress Java language access checking when it is used. A value of false indicates that the reflected object should enforce Java language access checks.

What are the drawbacks of reflection in Java?

Since reflection allows code to perform operations that would be illegal in non-reflective code, such as accessing private fields and methods, the use of reflection can result in unexpected side-effects, which may render code dysfunctional and may destroy portability.


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
    }

}

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.


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.