method - reflection java stack overflow
Reflejo de Java: impacto de setAccessible(verdadero) (4)
Estoy usando algunas anotaciones para establecer dinámicamente valores de campos en clases. Como quiero hacer esto independientemente de si es público, protegido o privado, estoy llamando a setAccessible(true)
en el objeto Field cada vez antes de llamar al método set()
. Mi pregunta es ¿qué tipo de impacto tiene la llamada setAccessible()
en el campo mismo?
Más específicamente, digamos que es un campo privado y este conjunto de códigos llama a setAccessible(true)
. Si algún otro lugar en el código fuera recuperar el mismo campo a través de la reflexión, ¿el campo ya estaría accesible? ¿O los getDeclaredFields()
y getDeclaredField()
devuelven nuevas instancias de un objeto Field cada vez?
Supongo que otra forma de plantear la pregunta es si llamo a setAccessible(true)
, ¿qué tan importante es volver a establecer el valor original después de que termine?
Como han indicado otros carteles, setAccessible
solo se aplica a esa instancia de su java.lang.reflect.Field
, por lo que no es necesario establecer la accesibilidad a su estado original.
Sin embargo...
Si desea que sus llamadas a field.setAccessible(true)
sean persistentes, debe usar los métodos subyacentes en java.lang.Class
y java.lang.reflect.Field
. Los métodos públicos le envían copias de la instancia de Field
, por lo que "se olvida" después de cada vez que hace algo como 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();
}
}
}
Con setAccessible()
cambia el comportamiento de AccessibleObject
, es decir, la instancia de Field
, pero no el campo real de la clase. Aquí está la documentation (extracto):
Un valor de
true
indica que el objeto reflejado debe suprimir las comprobaciones de control de acceso de lenguaje Java cuando se utiliza
Y un ejemplo ejecutable:
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));
Field field2 = myClass.getClass().getDeclaredField("theField");
System.out.println(field2.get(myClass));
}
}
El método getDeclaredField
debe devolver un nuevo objeto cada vez, exactamente porque este objeto tiene el marcador accessible
mutable. Entonces no hay necesidad de reiniciar la bandera. Puede encontrar todos los detalles en esta publicación de blog .
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));
}
}