How can I access an inherited protected field from an object by reflection?
问题:
回答1:
Two issues you may be having issues with - the field might not be accessible normally (private), and it's not in the class you are looking at, but somewhere up the hierarchy.
Something like this would work even with those issues:
public class SomeExample {
public static void main(String[] args) throws Exception{
Object myObj = new SomeDerivedClass(1234);
Class myClass = myObj.getClass();
Field myField = getField(myClass, "value");
myField.setAccessible(true); //required if field is not normally accessible
System.out.println("value: " + myField.get(myObj));
}
private static Field getField(Class clazz, String fieldName)
throws NoSuchFieldException {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
Class superClass = clazz.getSuperclass();
if (superClass == null) {
throw e;
} else {
return getField(superClass, fieldName);
}
}
}
}
class SomeBaseClass {
private Integer value;
SomeBaseClass(Integer value) {
this.value = value;
}
}
class SomeDerivedClass extends SomeBaseClass {
SomeDerivedClass(Integer value) {
super(value);
}
}
回答2:
Use reflection to access the members of the class instance, make them accessible and set their respective values. Of course you'd have to know the name of each member you want to change, but I guess that won't be a problem.
public class ReflectionUtil {
public static Field getField(Class clazz, String fieldName) throws NoSuchFieldException {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
Class superClass = clazz.getSuperclass();
if (superClass == null) {
throw e;
} else {
return getField(superClass, fieldName);
}
}
}
public static void makeAccessible(Field field) {
if (!Modifier.isPublic(field.getModifiers()) ||
!Modifier.isPublic(field.getDeclaringClass().getModifiers()))
{
field.setAccessible(true);
}
}
}
public class Application {
public static void main(String[] args) throws Exception {
KalaGameState obj = new KalaGameState();
Field field = ReflectionUtil.getField(obj.getClass(), 'turn');
ReflectionUtil.makeAccessible(field);
field.setInt(obj, 666);
System.out.println("turn is " + field.get(obj));
}
}
回答3:
field = myclass.getDeclaredField("myname");
field.setAccessible(true);
field.set(myinstance, newvalue);
回答4:
Use FieldUtils.writeField(object, "fieldname", value, true)
or readField(object, "fieldname", true)
from Apache Commons lang3.
回答5:
I didn't want to drag in more libraries so I made a pure one that worked for me. It is an extension of one of the methods from jweyrich:
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Date;
import java.util.Random;
import java.util.UUID;
public abstract class POJOFiller {
static final Random random = new Random();
public static void fillObject(Object ob) {
Class<? extends Object> clazz = ob.getClass();
do {
Field[] fields = clazz.getDeclaredFields();
fillForFields(ob, fields);
if (clazz.getSuperclass() == null) {
return;
}
clazz = clazz.getSuperclass();
} while (true);
}
private static void fillForFields(Object ob, Field[] fields) {
for (Field field : fields) {
field.setAccessible(true);
if(Modifier.isFinal(field.getModifiers())) {
continue;
}
try {
field.set(ob, generateRandomValue(field.getType()));
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
static Object generateRandomValue(Class<?> fieldType) {
if (fieldType.equals(String.class)) {
return UUID.randomUUID().toString();
} else if (Date.class.isAssignableFrom(fieldType)) {
return new Date(System.currentTimeMillis());
} else if (Number.class.isAssignableFrom(fieldType)) {
return random.nextInt(Byte.MAX_VALUE) + 1;
} else if (fieldType.equals(Integer.TYPE)) {
return random.nextInt();
} else if (fieldType.equals(Long.TYPE)) {
return random.nextInt();
} else if (Enum.class.isAssignableFrom(fieldType)) {
Object[] enumValues = fieldType.getEnumConstants();
return enumValues[random.nextInt(enumValues.length)];
} else if(fieldType.equals(Integer[].class)) {
return new Integer[] {random.nextInt(), random.nextInt()};
}
else {
throw new IllegalArgumentException("Cannot generate for " + fieldType);
}
}
}
回答6:
If you are just getting the protected field
Field protectedfield = Myclazz.class.getSuperclass().getDeclaredField("num");
If you are using Eclipse Ctrl + Space will bring up a list of methods when you type a "." after the object
回答7:
If using Spring, ReflectionTestUtils provides some handy tools that help out here with minimal effort.
For example, to get a protected field value that is known to be an int
:
int theIntValue = (int)ReflectionTestUtils.getField(theClass, "theProtectedIntField");
Or alternatively to set this field's value:
ReflectionTestUtils.setField(theClass, "theProtectedIntField", theIntValue);
回答8:
Do you perhaps mean from a different object an untrusted context with a SecurityManager
set? That would break the type system, so you can't. From a trusted context, you can call setAccessible
to defeat the type system. Ideally, don't use reflection.
回答9:
You could do something like...
Class clazz = Class.forName("SuperclassObject");
Field fields[] = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.getName().equals("fieldImLookingFor")) {
field.set...() // ... should be the type, eg. setDouble(12.34);
}
}
You might also need to change the accessibility, as noted in Maurice's answer.
回答10:
A generic utility method to run any getter in this or any superclass.
Adapted from Marius's answer.
public static Object RunGetter(String fieldname, Object o){
Object result = null;
boolean found = false;
//Search this and all superclasses:
for (Class<?> clas = o.getClass(); clas != null; clas = clas.getSuperclass()){
if(found){
break;
}
//Find the correct method:
for (Method method : clas.getDeclaredMethods()){
if(found){
break;
}
//Method found:
if ((method.getName().startsWith("get")) && (method.getName().length() == (fieldname.length() + 3))){
if (method.getName().toLowerCase().endsWith(fieldname.toLowerCase())){
try{
result = method.invoke(o); //Invoke Getter:
found = true;
} catch (IllegalAccessException | InvocationTargetException ex){
Logger.getLogger("").log(Level.SEVERE, "Could not determine method: " + method.getName(), ex);
}
}
}
}
}
return result;
}
Hopefully that is useful to someone.