Встановити значення приватного поля з відображенням


82

У мене є 2 класи: FatherіChild

public class Father implements Serializable, JSONInterface {

    private String a_field;

    //setter and getter here

}

public class Child extends Father {
    //empty class
}

З рефлексією я хочу поставити a_fieldв Childкласі:

Class<?> clazz = Class.forName("Child");
Object cc = clazz.newInstance();

Field f1 = cc.getClass().getField("a_field");
f1.set(cc, "reflecting on life");
String str1 = (String) f1.get(cc.getClass());
System.out.println("field: " + str1);

але у мене є виняток:

Виняток у потоці "main" java.lang.NoSuchFieldException: a_field

Але якщо я спробую:

Child child = new Child();
child.setA_field("123");

це працює.

За допомогою методу сеттера у мене така ж проблема:

method = cc.getClass().getMethod("setA_field");
method.invoke(cc, new Object[] { "aaaaaaaaaaaaaa" });

2
Спочатку потрібно отримати суперклас Класу, перш ніж мати змогу отримати доступ до поля. ще одне ТО питання
SomeJavaGuy

1
Поле не є частиною класу Child, оскільки воно приватне, воно навіть не видно в класі Child. Установці є загальнодоступними та передаються у спадок, що означає, що їх можна викликати так, ніби вони були оголошені в класі Child.
f1sh

Замість того, щоб отримувати приватне поле, встановіть його значення,
вибравши

Так, це хороше рішення. Тепер я можу спробувати
user1066183

Відповіді:


147

Для доступу до приватного поля потрібно встановити Field::setAccessibleзначення true. Ви можете витягнути поле з супер класу. Цей код працює:

Class<?> clazz = Child.class;
Object cc = clazz.newInstance();

Field f1 = cc.getClass().getSuperclass().getDeclaredField("a_field");
f1.setAccessible(true);
f1.set(cc, "reflecting on life");
String str1 = (String) f1.get(cc);
System.out.println("field: " + str1);

79

Використання FieldUtilsз Apache Commons Lang 3 :

FieldUtils.writeField(childInstance, "a_field", "Hello", true);

В trueсилах його встановити, навіть якщо поле приватних .


Слава Богу, за цей вихідний люк із тих ідіотських полів, які ПОВИННІ встановлювати ін’єкція залежності Spring Bean.
Шрідхар Сарнобат

4

Він також може отримати доступ до приватних полів, не роблячи нічого

import org.apache.commons.lang3.reflect.FieldUtils;
Object value = FieldUtils.readField(entity, fieldName, true);

3

Котлін версія

Отримайте приватну змінну, використовуючи функції розширення нижче

fun <T : Any> T.getPrivateProperty(variableName: String): Any? {
    return javaClass.getDeclaredField(variableName).let { field ->
        field.isAccessible = true
        return@let field.get(this)
    }
}

Встановити значення приватної змінної отримати змінну

fun <T : Any> T.setAndReturnPrivateProperty(variableName: String, data: Any): Any? {
    return javaClass.getDeclaredField(variableName).let { field ->
        field.isAccessible = true
        field.set(this, data)
        return@let field.get(this)
    }
}

Отримати змінне використання:

val bool = <your_class_object>.getPrivateProperty("your_variable") as String

Встановіть та отримайте використання змінних:

val bool = <your_class_object>.setAndReturnPrivateProperty("your_variable", true) as Boolean
val str = <your_class_object>.setAndReturnPrivateProperty("your_variable", "Hello") as String

Версія Java

public class RefUtil {

    public static Field setFieldValue(Object object, String fieldName, Object valueTobeSet) throws NoSuchFieldException, IllegalAccessException {
        Field field = getField(object.getClass(), fieldName);
        field.setAccessible(true);
        field.set(object, valueTobeSet);
        return field;
    }

    public static Object getPrivateFieldValue(Object object, String fieldName) throws NoSuchFieldException, IllegalAccessException {
        Field field = getField(object.getClass(), fieldName);
        field.setAccessible(true);
        return field.get(object);
    }

    private static Field getField(Class mClass, String fieldName) throws NoSuchFieldException {
        try {
            return mClass.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            Class superClass = mClass.getSuperclass();
            if (superClass == null) {
                throw e;
            } else {
                return getField(superClass, fieldName);
            }
        }
    }
}

Встановити використання приватного значення

RefUtil.setFieldValue(<your_class_object>, "your_variableName", newValue);

Отримайте приватне використання цінності

Object value = RefUtil.getPrivateFieldValue(<your_class_object>, "your_variableName");

1

Ви можете використовувати КОЛЛЕКТОР в @Jailbreak для прямого тіпобезопасного Java відображення:

@Jailbreak Child child = new Child();
child.a_field = "123;

@Jailbreakрозблоковує дочірню локальну змінну в компіляторі для прямого доступу до всіх членів Childієрархії.

Подібним чином ви можете використовувати метод розширення джейлбрейка () для одноразового використання:

child.jailbreak().a_field = "123";

За допомогою jailbreak()методу ви можете отримати доступ до будь-якого члена в Childієрархії.

В обох випадках компілятор вирішує для вас доступ до поля безпечно, ніби загальнодоступне поле, тоді як Manifold генерує ефективний код відображення для вас під капотом.

Дізнайтеся більше про колектор .


-5

Відповідно до Javadoc Class.getField(з наголосом):

Повертає об'єкт Field, що відображає вказане загальнодоступне поле-член класу або інтерфейсу, представленого цим об'єктом Class.

Цей метод повертає лише загальнодоступні поля. Оскільки a_fieldприватний, його не знайдете.

Ось робочий код:

public class Main {

    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("Child");
        Object cc = clazz.newInstance();

        Field f1 = cc.getClass().getField("a_field");
        f1.set(cc, "reflecting on life");
        String str1 = (String) f1.get(cc);
        System.out.println("field: " + str1);
    }

}

class Father implements Serializable {
    public String a_field;
}

class Child extends Father {
//empty class
}

Зверніть увагу, що я також змінив ваш рядок String str1 = (String) f1.get(cc.getClass());на, String str1 = (String) f1.get(cc);тому що вам потрібно вказати об'єкт поля, а не клас.


Якщо ви хочете зберегти своє поле приватним, вам потрібно отримати метод getter / setter і замість цього викликати їх. Код, який ви вказали, не працює, тому що, щоб отримати метод, вам також потрібно вказати його аргументи, отже

cc.getClass().getMethod("setA_field");

повинно бути

cc.getClass().getMethod("setA_field", String.class);

Ось робочий код:

public class Main {

    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("Child");
        Object cc = clazz.newInstance();
        cc.getClass().getMethod("setA_field", String.class).invoke(cc, "aaaaaaaaaaaaaa");
        String str1 = (String) cc.getClass().getMethod("getA_field").invoke(cc);
        System.out.println("field: " + str1);
    }

}

class Father implements Serializable {

    private String a_field;

    public String getA_field() {
        return a_field;
    }

    public void setA_field(String a_field) {
        this.a_field = a_field;
    }

}

class Child extends Father {
    //empty class
}

дякую .. але a_field має бути приватним. Як варіант, чи можу я викликати метод сетера?
user1066183
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.