リフレクションによってオブジェクトから継承された保護フィールドにアクセスするにはどうすればよいですか?
問題が発生している可能性のある2つの問題-フィールドは通常はアクセスできない(プライベート)可能性があります。また、フィールドは現在のクラスではなく、階層のどこかにあります。
このようなものはそれらの問題でも機能します:
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);
}
}
Apache Commons lang のFieldUtils.writeField(object, "fieldname", value, true)
またはreadField(object, "fieldname", true)
を使用します。
リフレクションを使用して、クラスインスタンスのメンバーにアクセスし、それらをアクセス可能にして、それぞれの値を設定します。もちろん、変更したい各メンバーの名前を知っている必要がありますが、それは問題ではないと思います。
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));
}
}
field = myclass.getDeclaredField("myname");
field.setAccessible(true);
field.set(myinstance, newvalue);
このユーティリティを使用します。
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);
}
}
ライブラリを追加したくなかったので、自分に合った純粋なライブラリを作成しました。これは、jweyrichのメソッドの1つを拡張したものです。
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 「。」を入力すると、メソッドのリストが表示されます。オブジェクトの後
Springを使用している場合、 ReflectionTestUtils は、最小限の労力でここで役立ついくつかの便利なツールを提供します。
たとえば、int
であることがわかっている保護フィールド値を取得するには:
int theIntValue = (int)ReflectionTestUtils.getField(theClass, "theProtectedIntField");
または、このフィールドの値を設定するには:
ReflectionTestUtils.setField(theClass, "theProtectedIntField", theIntValue);
このまたは任意のスーパークラスで任意のゲッターを実行するための汎用ユーティリティメソッド
Marius's の回答から転載。
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;
}
うまくいけば、それは誰かにとって便利です。
別のオブジェクトから、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);
}
}
モーリスの回答で述べられているように、アクセシビリティを変更する必要があるかもしれません。