Stellen Sie den privaten Feldwert mit Reflexion ein

76

Ich habe 2 Klassen: FatherundChild

public class Father implements Serializable, JSONInterface {

    private String a_field;

    //setter and getter here

}

public class Child extends Father {
    //empty class
}

Mit Reflexion möchte ich a_fieldin der ChildKlasse setzen:

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);

aber ich habe eine ausnahme:

Ausnahme im Thread "main" java.lang.NoSuchFieldException: a_field

Aber wenn ich es versuche:

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

Es klappt.

Mit der Setter-Methode habe ich das gleiche Problem:

method = cc.getClass().getMethod("setA_field");
method.invoke(cc, new Object[] { "aaaaaaaaaaaaaa" });
user1066183
quelle
2
Sie müssen zuerst die Oberklasse der Klasse erhalten, bevor Sie das Feld betreten können. eine andere SO-Frage
SomeJavaGuy
1
Das Feld ist nicht Teil der Child-Klasse. Da es privat ist, ist es in der Child-Klasse nicht einmal sichtbar. Die Setter sind öffentlich und werden geerbt. Dies bedeutet, dass sie aufgerufen werden können, als wären sie in der Child-Klasse deklariert.
f1sh
Anstatt das Feld abzurufen, das privat ist, setzen Sie seinen Wert, indem Sie seine Setter-Methode
abrufen
Ja, das ist eine gute Lösung. Jetzt kann ich versuchen
user1066183

Antworten:

141

Um auf ein privates Feld zuzugreifen, müssen Sie Field::setAccessibletrue festlegen . Sie können das Feld aus der Superklasse ziehen. Dieser Code funktioniert:

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);
John McClean
quelle
75

Verwenden FieldUtilsvon Apache Commons Lang 3 :

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

Das trueerzwingt das Setzen, auch wenn das Feld privat ist .

David Lavender
quelle
Gott sei Dank für diese Notluke aus diesen idiotischen Feldern, die durch Spring Bean-Abhängigkeitsinjektion gesetzt werden MÜSSEN.
Sridhar Sarnobat
4

Dieser kann auch auf private Felder zugreifen, ohne etwas tun zu müssen

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

Kotlin Verison

Holen Sie sich eine private Variable mit den folgenden Erweiterungsfunktionen

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

Setzen Sie den Wert der privaten Variablen, um die Variable abzurufen

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)
    }
}

Variable Verwendung erhalten:

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

Festlegen und Abrufen der variablen Verwendung:

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

Java-Version

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);
            }
        }
    }
}

Legen Sie die Verwendung privater Werte fest

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

Holen Sie sich private Wertnutzung

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

Sie können Manifolds @Jailbreak für die direkte, typsichere Java-Reflexion verwenden:

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

@Jailbreak Schaltet die untergeordnete lokale Variable im Compiler für den direkten Zugriff auf alle Mitglieder in frei Child der Hierarchie frei.

Ebenso können Sie die Erweiterungsmethode jailbreak () für die einmalige Verwendung verwenden:

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

Über die jailbreak()Methode können Sie auf jedes Mitglied in Childder Hierarchie zugreifen .

In beiden Fällen löst der Compiler den Feldzugriff für Sie typsicher wie ein öffentliches Feld auf, während Manifold unter der Haube einen effizienten Reflektionscode für Sie generiert.

Erfahren Sie mehr über Manifold .

Scott
quelle
-5

Nach dem Javadoc von Class.getField(Schwerpunkt Mine):

Gibt ein Feldobjekt zurück, das das angegebene öffentliche Mitgliedsfeld der Klasse oder Schnittstelle widerspiegelt, die von diesem Klassenobjekt dargestellt wird.

Diese Methode gibt nur öffentliche Felder zurück. Da a_fieldes privat ist, wird es nicht gefunden.

Hier ist ein Arbeitscode:

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
}

Beachten Sie, dass ich auch Ihre Zeile String str1 = (String) f1.get(cc.getClass());in geändert habe , String str1 = (String) f1.get(cc);weil Sie das Objekt des Felds und nicht die Klasse angeben müssen.


Wenn Sie Ihr Feld privat halten möchten, müssen Sie die Getter / Setter-Methode abrufen und stattdessen diese aufrufen. Der von Ihnen angegebene Code funktioniert nicht, da Sie zum Abrufen einer Methode auch deren Argumente angeben müssen

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

muss sein

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

Hier ist ein Arbeitscode:

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
}
Tunaki
quelle
danke .. aber a_field muss privat sein. Kann ich alternativ die Setter-Methode aufrufen?
user1066183