如何通过反射从对象访问继承的受保护字段?
11 回答
您可能遇到的两个问题 - 该字段可能无法正常访问(私有),并且它不在您正在查看的类中,而是在层次结构的某个位置。
即使有这些问题,这样的事情也会起作用:
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);
}
}
使用FieldUtils.writeField(object, "fieldname", value, true)
或readField(object, "fieldname", true)
来自Apache Commons lang3。
field = myclass.getDeclaredField("myname");
field.setAccessible(true);
field.set(myinstance, newvalue);
使用反射访问类实例的成员,使它们可访问并设置它们各自的值。当然,您必须知道要更改的每个成员的名称,但我想这不会有问题。
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));
}
}
如果使用 Spring,ReflectionTestUtils提供了一些方便的工具,可以帮助您轻松完成任务。
例如,要获取已知为 的受保护字段值int
:
int theIntValue = (int)ReflectionTestUtils.getField(theClass, "theProtectedIntField");
或者设置此字段的值:
ReflectionTestUtils.setField(theClass, "theProtectedIntField", theIntValue);
我不想拖入更多的库,所以我制作了一个适合我的纯库。它是 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);
}
}
}
如果您只是获得受保护的领域
Field protectedfield = Myclazz.class.getSuperclass().getDeclaredField("num");
如果您使用的是 Eclipse Ctrl+ Space,当您键入“.”时会弹出一个方法列表。在对象之后
使用此实用程序:
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Stream;
import static java.lang.String.format;
public final class ReflectionUtils {
private ReflectionUtils() { }
private static final String GETTER_PREFIX = "get";
private static final String SETTER_PREFIX = "set";
/**
* Get name of getter
*
* @param fieldName fieldName
* @return getter name
*/
public static String getterByFieldName(String fieldName) {
if (isStringNullOrEmpty(fieldName))
return null;
return convertFieldByAddingPrefix(fieldName, GETTER_PREFIX);
}
/**
* Get name of setter
*
* @param fieldName fieldName
* @return setter name
*/
public static String setterByFieldName(String fieldName) {
if (isStringNullOrEmpty(fieldName))
return null;
return convertFieldByAddingPrefix(fieldName, SETTER_PREFIX);
}
/**
* Get the contents of the field with any access modifier
*
* @param obj obj
* @param fieldName fieldName
* @return content of field
*/
public static Object getFieldContent(Object obj, String fieldName) {
if (!isValidParams(obj, fieldName))
return null;
try {
Field declaredField = getFieldAccessible(obj, fieldName);
return declaredField.get(obj);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Cannot get field content for field name: " + fieldName, e);
}
}
/**
* @param clazz clazz
* @param fieldName fieldName
* @return content static field
*/
public static Object getStaticFieldContent(final Class<?> clazz, final String fieldName) {
try {
Field field = getFieldWithCheck(clazz, fieldName);
field.setAccessible(true);
return field.get(clazz);
} catch (Exception e) {
String exceptionMsg = format("Cannot find or get static field: '%s' from class: '%s'", fieldName, clazz);
throw new RuntimeException(exceptionMsg, e);
}
}
/**
* Set the contents to the field with any access modifier
*
* @param obj obj
* @param fieldName fieldName
* @param value value
*/
public static void setFieldContent(Object obj, String fieldName, Object value) {
if (!isValidParams(obj, fieldName))
return;
try {
Field declaredField = getFieldAccessible(obj, fieldName);
declaredField.set(obj, value);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Cannot set field content for field name: " + fieldName, e);
}
}
/**
* Call a method with any access modifier
*
* @param obj obj
* @param methodName methodName
* @return result of method
*/
public static Object callMethod(Object obj, String methodName) {
if (!isValidParams(obj, methodName))
return null;
try {
Method method = obj.getClass().getMethod(methodName);
method.setAccessible(true);
return method.invoke(obj);
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new IllegalArgumentException("Cannot invoke method name: " + methodName, e);
}
}
/**
* Get all fields even from parent
*
* @param clazz clazz
* @return array of fields
*/
public static Field[] getAllFields(Class<?> clazz) {
if (clazz == null) return null;
List<Field> fields = new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()));
if (clazz.getSuperclass() != null) {
// danger! Recursion
fields.addAll(Arrays.asList(getAllFields(clazz.getSuperclass())));
}
return fields.toArray(new Field[] {});
}
/**
* Get the Field from Object even from parent
*
* @param obj obj
* @param fieldName fieldName
* @return {@code Optional}
*/
public static Optional<Field> getField(Object obj, String fieldName) {
if (!isValidParams(obj, fieldName))
return Optional.empty();
Class<?> clazz = obj.getClass();
return getField(clazz, fieldName);
}
/**
* Get the Field from Class even from parent
*
* @param clazz clazz
* @param fieldName fieldName
* @return {@code Optional}
*/
public static Optional<Field> getField(Class<?> clazz, String fieldName) {
if (!isValidParams(clazz, fieldName))
return Optional.empty();
Field[] fields = getAllFields(clazz);
return Stream.of(fields)
.filter(x -> x.getName().equals(fieldName))
.findFirst();
}
/**
* @param clazz clazz
* @param fieldName fieldName
* @return Class
*/
public static Class<?> getFieldType(Class<?> clazz, String fieldName) {
return getFieldWithCheck(clazz, fieldName).getType();
}
/**
* @param clazz clazz
* @param fieldName fieldName
* @return Field
*/
public static Field getFieldWithCheck(Class<?> clazz, String fieldName) {
return ReflectionUtils.getField(clazz, fieldName)
.orElseThrow(() -> {
String msg = String.format("Cannot find field name: '%s' from class: '%s'", fieldName, clazz);
return new IllegalArgumentException(msg);
});
}
/**
* Get the field values with the types already listed according to the field type
*
* @param clazz clazz
* @param fieldName fieldName
* @param fieldValue fieldValue
* @return value cast to specific field type
*/
public static Object castFieldValueByClass(Class<?> clazz, String fieldName, Object fieldValue) {
Field field = getField(clazz, fieldName)
.orElseThrow(() -> new IllegalArgumentException(String.format("Cannot find field by name: '%s'", fieldName)));
Class<?> fieldType = field.getType();
return castFieldValueByType(fieldType, fieldValue);
}
/**
* @param fieldType fieldType
* @param fieldValue fieldValue
* @return casted value
*/
public static Object castFieldValueByType(Class<?> fieldType, Object fieldValue) {
if (fieldType.isAssignableFrom(Boolean.class)) {
if (fieldValue instanceof String) {
return convertStringToBoolean((String) fieldValue);
}
if (fieldValue instanceof Number) {
return !(fieldValue).equals(0);
}
return fieldValue;
}
else if (fieldType.isAssignableFrom(Double.class)) {
if (fieldValue instanceof String) {
return Double.valueOf((String)fieldValue);
}
return ((Number) fieldValue).doubleValue();
}
else if (fieldType.isAssignableFrom(Long.class)) {
if (fieldValue instanceof String) {
return Long.valueOf((String)fieldValue);
}
return ((Number) fieldValue).longValue();
}
else if (fieldType.isAssignableFrom(Float.class)) {
if (fieldValue instanceof String) {
return Float.valueOf((String)fieldValue);
}
return ((Number) fieldValue).floatValue();
}
else if (fieldType.isAssignableFrom(Integer.class)) {
if (fieldValue instanceof String) {
return Integer.valueOf((String)fieldValue);
}
return ((Number) fieldValue).intValue();
}
else if (fieldType.isAssignableFrom(Short.class)) {
if (fieldValue instanceof String) {
return Short.valueOf((String)fieldValue);
}
return ((Number) fieldValue).shortValue();
}
return fieldValue;
}
private static boolean convertStringToBoolean(String s) {
String trim = s.trim();
return !trim.equals("") && !trim.equals("0") && !trim.toLowerCase().equals("false");
}
private static boolean isValidParams(Object obj, String param) {
return (obj != null && !isStringNullOrEmpty(param));
}
private static boolean isStringNullOrEmpty(String fieldName) {
return fieldName == null || fieldName.trim().length() == 0;
}
private static Field getFieldAccessible(Object obj, String fieldName) {
Optional<Field> optionalField = getField(obj, fieldName);
return optionalField
.map(el -> {
el.setAccessible(true);
return el;
})
.orElseThrow(() -> new IllegalArgumentException("Cannot find field name: " + fieldName));
}
private static String convertFieldByAddingPrefix(String fieldName, String prefix) {
return prefix + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
}
}
您是否可能是指来自不同对象的带有SecurityManager
集合的不受信任的上下文?那会破坏类型系统,所以你不能。从受信任的上下文中,您可以调用setAccessible
来破坏类型系统。理想情况下,不要使用反射。
你可以做类似...
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);
}
}
如莫里斯的回答中所述,您可能还需要更改可访问性。
在这个或任何超类中运行任何 getter 的通用实用程序方法。
改编自马吕斯的回答。
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;
}
希望这对某人有用。