Anleitung zu Java Reflection
1. Überblick
In diesem Artikel befassen wir uns mit Java Reflection, mit der wir Laufzeitattribute von Klassen, Interfaces, Feldern und Methoden untersuchen oder / und ändern können. Dies ist besonders nützlich, wenn wir ihre Namen beim Kompilieren nicht kennen.
Darüber hinaus können wir neue Objekte instanziieren, Methoden aufrufen und Feldwerte mithilfe von Reflektion abrufen oder festlegen.
2. Projektaufbau
To use java reflection, we do not need to include any special jars, spezielle Konfigurationen oder Maven-Abhängigkeiten. Das JDK wird mit einer Gruppe von Klassen geliefert, die speziell für diesen Zweck im Paketjava.lang.reflect enthalten sind.
Wir müssen also nur den folgenden Import in unseren Code durchführen:
import java.lang.reflect.*;
und wir sind gut zu gehen.
Um Zugriff auf die Klassen-, Methoden- und Feldinformationen einer Instanz zu erhalten, rufen wir diegetClass-Methode auf, die die Laufzeitklassendarstellung des Objekts zurückgibt. Das zurückgegebeneclass-Objekt bietet Methoden für den Zugriff auf Informationen zu einer Klasse.
3. Einfaches Beispiel
Um unsere Füße nass zu machen, schauen wir uns ein sehr einfaches Beispiel an, das zur Laufzeit die Felder eines einfachen Java-Objekts untersucht.
Erstellen wir eine einfachePerson-Klasse mit nurname- undage-Feldern und überhaupt keinen Methoden. Hier ist die Personenklasse:
public class Person {
private String name;
private int age;
}
Wir werden nun Java Reflection verwenden, um die Namen aller Felder dieser Klasse zu ermitteln. Um die Reflexionskraft zu schätzen, konstruieren wir einPerson-Objekt und verwenden Object als Referenztyp:
@Test
public void givenObject_whenGetsFieldNamesAtRuntime_thenCorrect() {
Object person = new Person();
Field[] fields = person.getClass().getDeclaredFields();
List actualFieldNames = getFieldNames(fields);
assertTrue(Arrays.asList("name", "age")
.containsAll(actualFieldNames));
}
Dieser Test zeigt uns, dass wir in der Lage sind, ein Array vonField Objekten aus unseremperson Objekt abzurufen, selbst wenn der Verweis auf das Objekt ein übergeordneter Typ dieses Objekts ist.
Im obigen Beispiel waren wir nur an den Namen dieser Felder interessiert, aber es gibt noch viel mehr zu tun, und wir werden in den folgenden Abschnitten weitere Beispiele dafür sehen.
Beachten Sie, wie wir eine Hilfsmethode verwenden, um die tatsächlichen Feldnamen zu extrahieren. Dies ist ein sehr grundlegender Code:
private static List getFieldNames(Field[] fields) {
List fieldNames = new ArrayList<>();
for (Field field : fields)
fieldNames.add(field.getName());
return fieldNames;
}
4. Anwendungsfälle für Java Reflection
Bevor wir zu den verschiedenen Merkmalen der Java-Reflektion übergehen, werden wir einige der gebräuchlichen Verwendungen erörtern, die wir möglicherweise dafür finden. Java Reflection ist extrem leistungsfähig und kann in vielerlei Hinsicht sehr nützlich sein.
In vielen Fällen haben wir beispielsweise eine Namenskonvention für Datenbanktabellen. Wir können uns dafür entscheiden, Konsistenz hinzuzufügen, indem wir unsere Tabellennamen mittbl_ vorab festlegen, sodass eine Tabelle mit Schülerdatentbl_student_data heißt.
In solchen Fällen können wir das Java-Objekt, das Schülerdaten enthält, alsStudent oderStudentData. bezeichnen. Unter Verwendung des CRUD-Paradigmas haben wir dann einen Einstiegspunkt für jede Operation, sodassCreate Operationen nur eine erhalten Object Parameter.
Anschließend verwenden wir Reflection, um den Objektnamen und die Feldnamen abzurufen. Zu diesem Zeitpunkt können wir diese Daten einer DB-Tabelle zuordnen und die Objektfeldwerte den entsprechenden DB-Feldnamen zuweisen.
5. Überprüfen von Java-Klassen
In diesem Abschnitt werden wir die grundlegendste Komponente der Java Reflection-API untersuchen. Java-Klassenobjekte geben uns, wie bereits erwähnt, Zugriff auf die internen Details eines Objekts.
Wir werden interne Details wie den Klassennamen eines Objekts, seine Modifikatoren, Felder, Methoden, implementierten Schnittstellen usw. untersuchen.
5.1. Fertig werden
Um die Reflection-API, die auf Java-Klassen angewendet wird und Beispiele mit Vielfalt enthält, fest im Griff zu haben, erstellen wir eine abstrakteAnimal-Klasse, die dieEating-Schnittstelle implementiert. Diese Schnittstelle definiert das Essverhalten jedes von uns erstellten konkretenAnimal-Objekts.
Hier ist also zunächst dieEating-Schnittstelle:
public interface Eating {
String eats();
}
und dann die konkrete Implementierung derEating-Schnittstelle durchAnimal:
public abstract class Animal implements Eating {
public static String CATEGORY = "domestic";
private String name;
protected abstract String getSound();
// constructor, standard getters and setters omitted
}
Erstellen wir auch eine andere Schnittstelle namensLocomotion, die beschreibt, wie sich ein Tier bewegt:
public interface Locomotion {
String getLocomotion();
}
Wir werden nun eine konkrete Klasse namensGoat erstellen, dieAnimal erweitert undLocomotion implementiert. Da die SuperklasseEating implementiert, müssenGoat auch die Methoden dieser Schnittstelle implementieren:
public class Goat extends Animal implements Locomotion {
@Override
protected String getSound() {
return "bleat";
}
@Override
public String getLocomotion() {
return "walks";
}
@Override
public String eats() {
return "grass";
}
// constructor omitted
}
Ab diesem Zeitpunkt werden wir Java Reflection verwenden, um Aspekte von Java-Objekten zu untersuchen, die in den oben genannten Klassen und Schnittstellen angezeigt werden.
5.2. Klassennamen
Beginnen wir mit dem Abrufen des Namens eines Objekts ausClass:
@Test
public void givenObject_whenGetsClassName_thenCorrect() {
Object goat = new Goat("goat");
Class> clazz = goat.getClass();
assertEquals("Goat", clazz.getSimpleName());
assertEquals("com.example.reflection.Goat", clazz.getName());
assertEquals("com.example.reflection.Goat", clazz.getCanonicalName());
}
Beachten Sie, dass diegetSimpleName-Methode vonClass den Basisnamen des Objekts zurückgibt, wie er in der Deklaration erscheinen würde. Dann geben die beiden anderen Methoden den vollständig qualifizierten Klassennamen einschließlich der Paketdeklaration zurück.
Lassen Sie uns auch sehen, wie wir ein Objekt der KlasseGoaterstellen können, wenn wir nur den vollständig qualifizierten Klassennamen kennen:
@Test
public void givenClassName_whenCreatesObject_thenCorrect(){
Class> clazz = Class.forName("com.example.reflection.Goat");
assertEquals("Goat", clazz.getSimpleName());
assertEquals("com.example.reflection.Goat", clazz.getName());
assertEquals("com.example.reflection.Goat", clazz.getCanonicalName());
}
Beachten Sie, dass der Name, den wir an die statischeforName-Methode übergeben, die Paketinformationen enthalten sollte, andernfalls erhalten wirClassNotFoundException.
5.3. Klassenmodifikatoren
Wir können die für eine Klasse verwendeten Modifikatoren bestimmen, indem wir die MethodegetModifiers aufrufen, dieInteger. zurückgibt. Jeder Modifikator ist ein Flag-Bit, das entweder gesetzt oder gelöscht wird.
Die Klassejava.lang.reflect.Modifier bietet statische Methoden, mit denen die zurückgegebenenInteger auf das Vorhandensein oder Fehlen eines bestimmten Modifikators analysiert werden.
Bestätigen wir die Modifikatoren einiger der oben definierten Klassen:
@Test
public void givenClass_whenRecognisesModifiers_thenCorrect() {
Class> goatClass = Class.forName("com.example.reflection.Goat");
Class> animalClass = Class.forName("com.example.reflection.Animal");
int goatMods = goatClass.getModifiers();
int animalMods = animalClass.getModifiers();
assertTrue(Modifier.isPublic(goatMods));
assertTrue(Modifier.isAbstract(animalMods));
assertTrue(Modifier.isPublic(animalMods));
}
Wir sind in der Lage, Modifikatoren jeder Klasse zu untersuchen, die sich in einer Bibliothek befinden, die wir in unser Projekt importieren.
In den meisten Fällen müssen wir möglicherweise denforName-Ansatz anstelle der vollständigen Instanziierung verwenden, da dies bei speicherintensiven Klassen ein teurer Prozess wäre.
5.4. Paketinformationen
Durch die Verwendung von Java Reflection können wir auch Informationen über das Paket einer Klasse oder eines Objekts erhalten. Diese Daten werden in der KlassePackagegebündelt, die durch einen Aufruf der MethodegetPackagefür das Klassenobjekt zurückgegeben wird.
Führen Sie einen Test aus, um den Paketnamen abzurufen:
@Test
public void givenClass_whenGetsPackageInfo_thenCorrect() {
Goat goat = new Goat("goat");
Class> goatClass = goat.getClass();
Package pkg = goatClass.getPackage();
assertEquals("com.example.reflection", pkg.getName());
}
5.5. Super Klasse
Wir sind auch in der Lage, die Superklasse jeder Java-Klasse durch Verwendung der Java-Reflexion zu erhalten.
In vielen Fällen, insbesondere bei der Verwendung von Bibliotheksklassen oder der in Java integrierten Klassen, kennen wir die Superklasse eines von uns verwendeten Objekts möglicherweise nicht im Voraus. In diesem Unterabschnitt wird gezeigt, wie diese Informationen abgerufen werden.
Lassen Sie uns also die Superklasse vonGoat, zusätzlich bestimmen. Wir zeigen auch, dass die Klasse vonjava.lang.Stringeine Unterklasse der Klasse vonjava.lang.Objectist:
@Test
public void givenClass_whenGetsSuperClass_thenCorrect() {
Goat goat = new Goat("goat");
String str = "any string";
Class> goatClass = goat.getClass();
Class> goatSuperClass = goatClass.getSuperclass();
assertEquals("Animal", goatSuperClass.getSimpleName());
assertEquals("Object", str.getClass().getSuperclass().getSimpleName());
}
5.6. Implementierte Schnittstellen
Mit Java-Reflexion können wir auchget the list of interfaces implemented by a given class.
Lassen Sie uns die Klassentypen der Schnittstellen abrufen, die von der KlasseGoatund der abstrakten KlasseAnimalimplementiert wurden:
@Test
public void givenClass_whenGetsImplementedInterfaces_thenCorrect(){
Class> goatClass = Class.forName("com.example.reflection.Goat");
Class> animalClass = Class.forName("com.example.reflection.Animal");
Class>[] goatInterfaces = goatClass.getInterfaces();
Class>[] animalInterfaces = animalClass.getInterfaces();
assertEquals(1, goatInterfaces.length);
assertEquals(1, animalInterfaces.length);
assertEquals("Locomotion", goatInterfaces[0].getSimpleName());
assertEquals("Eating", animalInterfaces[0].getSimpleName());
}
Beachten Sie aus den Behauptungen, dass jede Klasse nur eine einzige Schnittstelle implementiert. Wenn wir die Namen dieser Schnittstellen untersuchen, stellen wir fest, dassGoatLocomotion undAnimalEating implementiert, so wie es in unserem Code erscheint.
Möglicherweise haben Sie beobachtet, dassGoat eine Unterklasse der abstrakten KlasseAnimal ist und die Schnittstellenmethodeeats() implementiert. Dann implementiertGoat auch die SchnittstelleEating.
Es ist daher anzumerken, dass nur die Schnittstellen, die eine Klasse explizit als mit dem Schlüsselwortimplementsimplementiert als deklariert deklariert, im zurückgegebenen Array angezeigt werden.
Selbst wenn eine Klasse Schnittstellenmethoden implementiert, weil ihre Superklasse diese Schnittstelle implementiert, die Unterklasse diese Schnittstelle jedoch nicht direkt mit dem Schlüsselwortimplementsdeklariert, wird diese Schnittstelle nicht im Array der Schnittstellen angezeigt.
5.7. Konstruktoren, Methoden und Felder
Mit Java Reflection können wir die Konstruktoren der Klasse eines Objekts sowie Methoden und Felder untersuchen.
Wir werden später in der Lage sein, eingehendere Inspektionen für jede dieser Komponenten einer Klasse zu sehen, aber im Moment reicht es aus, nur ihre Namen zu ermitteln und sie mit dem zu vergleichen, was wir erwarten.
Mal sehen, wie man den Konstruktor derGoat-Klasse erhält:
@Test
public void givenClass_whenGetsConstructor_thenCorrect(){
Class> goatClass = Class.forName("com.example.reflection.Goat");
Constructor>[] constructors = goatClass.getConstructors();
assertEquals(1, constructors.length);
assertEquals("com.example.reflection.Goat", constructors[0].getName());
}
Wir können auch die Felder derAnimal-Klasse wie folgt untersuchen:
@Test
public void givenClass_whenGetsFields_thenCorrect(){
Class> animalClass = Class.forName("com.example.java.reflection.Animal");
Field[] fields = animalClass.getDeclaredFields();
List actualFields = getFieldNames(fields);
assertEquals(2, actualFields.size());
assertTrue(actualFields.containsAll(Arrays.asList("name", "CATEGORY")));
}
Genau wie wir die Methoden der KlasseAnimaluntersuchen können:
@Test
public void givenClass_whenGetsMethods_thenCorrect(){
Class> animalClass = Class.forName("com.example.java.reflection.Animal");
Method[] methods = animalClass.getDeclaredMethods();
List actualMethods = getMethodNames(methods);
assertEquals(4, actualMethods.size());
assertTrue(actualMethods.containsAll(Arrays.asList("getName",
"setName", "getSound")));
}
Genau wiegetFieldNames haben wir eine Hilfsmethode hinzugefügt, um Methodennamen aus einem Array vonMethod Objekten abzurufen:
private static List getMethodNames(Method[] methods) {
List methodNames = new ArrayList<>();
for (Method method : methods)
methodNames.add(method.getName());
return methodNames;
}
6. Konstruktoren prüfen
Mit Java-Reflexion können wirinspect constructors jeder Klasse und sogarcreate class objects at runtime. Dies wird durch die Klassejava.lang.reflect.Constructorermöglicht.
Früher haben wir uns nur angesehen, wie man das Array vonConstructor-Objekten erhält, von denen wir die Namen der Konstruktoren erhalten konnten.
In diesem Abschnitt konzentrieren wir uns darauf, wie bestimmte Konstruktoren abgerufen werden. Wie wir wissen, haben in Java keine zwei Konstruktoren einer Klasse genau dieselbe Methodensignatur. Wir werden diese Einzigartigkeit nutzen, um einen Konstruktor aus vielen zu erhalten.
Um die Funktionen dieser Klasse zu verstehen, erstellen wir eineBird-Unterklasse vonAnimal mit drei Konstruktoren. Wir werdenLocomotion nicht implementieren, damit wir dieses Verhalten mithilfe eines Konstruktorarguments angeben können, um noch mehr Abwechslung hinzuzufügen:
public class Bird extends Animal {
private boolean walks;
public Bird() {
super("bird");
}
public Bird(String name, boolean walks) {
super(name);
setWalks(walks);
}
public Bird(String name) {
super(name);
}
public boolean walks() {
return walks;
}
// standard setters and overridden methods
}
Lassen Sie uns durch Überlegungen bestätigen, dass diese Klasse drei Konstruktoren hat:
@Test
public void givenClass_whenGetsAllConstructors_thenCorrect() {
Class> birdClass = Class.forName("com.example.reflection.Bird");
Constructor>[] constructors = birdClass.getConstructors();
assertEquals(3, constructors.length);
}
Als Nächstes rufen wir jeden Konstruktor für die KlasseBirdab, indem wir die Parameterklassentypen des Konstruktors in deklarierter Reihenfolge übergeben:
@Test
public void givenClass_whenGetsEachConstructorByParamTypes_thenCorrect(){
Class> birdClass = Class.forName("com.example.reflection.Bird");
Constructor> cons1 = birdClass.getConstructor();
Constructor> cons2 = birdClass.getConstructor(String.class);
Constructor> cons3 = birdClass.getConstructor(String.class, boolean.class);
}
Es besteht keine Notwendigkeit für eine Bestätigung, da einNoSuchMethodException angezeigt wird, wenn ein Konstruktor mit bestimmten Parametertypen in der angegebenen Reihenfolge nicht vorhanden ist, und der Test automatisch fehlschlägt.
Im letzten Test werden wir sehen, wie Objekte zur Laufzeit instanziiert werden, während ihre Parameter angegeben werden:
@Test
public void givenClass_whenInstantiatesObjectsAtRuntime_thenCorrect() {
Class> birdClass = Class.forName("com.example.reflection.Bird");
Constructor> cons1 = birdClass.getConstructor();
Constructor> cons2 = birdClass.getConstructor(String.class);
Constructor> cons3 = birdClass.getConstructor(String.class,
boolean.class);
Bird bird1 = (Bird) cons1.newInstance();
Bird bird2 = (Bird) cons2.newInstance("Weaver bird");
Bird bird3 = (Bird) cons3.newInstance("dove", true);
assertEquals("bird", bird1.getName());
assertEquals("Weaver bird", bird2.getName());
assertEquals("dove", bird3.getName());
assertFalse(bird1.walks());
assertTrue(bird3.walks());
}
Wir instanziieren Klassenobjekte, indem wir dienewInstance-Methode derConstructor-Klasse aufrufen und die erforderlichen Parameter in deklarierter Reihenfolge übergeben. Wir wandeln das Ergebnis dann in den gewünschten Typ um.
Fürbird1 verwenden wir den Standardkonstruktor, der aus unseremBird-Code automatisch den Namen auf bird setzt und dies mit einem Test bestätigt.
Wir instanziieren dannbird2 nur mit einem Namen und einem Test. Denken Sie daran, dass wir das Fortbewegungsverhalten nicht auf false setzen, da es standardmäßig false ist, wie in den letzten beiden Aussagen gezeigt.
7. Felder inspizieren
Bisher haben wir nur die Namen der Felder in diesem Abschnittwe will show how toget and set their values at runtime überprüft.
Es gibt zwei Hauptmethoden, mit denen Felder einer Klasse zur Laufzeit überprüft werden:getFields() undgetField(fieldName).
Die MethodegetFields() gibt alle zugänglichen öffentlichen Felder der betreffenden Klasse zurück. Es werden alle öffentlichen Felder sowohl in der Klasse als auch in allen Superklassen zurückgegeben.
Wenn wir diese Methode beispielsweise für die KlasseBird aufrufen, erhalten wir nur das FeldCATEGORYder SuperklasseAnimal, daBird selbst keine deklariert öffentliche Felder:
@Test
public void givenClass_whenGetsPublicFields_thenCorrect() {
Class> birdClass = Class.forName("com.example.reflection.Bird");
Field[] fields = birdClass.getFields();
assertEquals(1, fields.length);
assertEquals("CATEGORY", fields[0].getName());
}
Diese Methode hat auch eine Variante namensgetField, die nur einField-Objekt zurückgibt, indem sie den Namen des Feldes verwendet:
@Test
public void givenClass_whenGetsPublicFieldByName_thenCorrect() {
Class> birdClass = Class.forName("com.example.reflection.Bird");
Field field = birdClass.getField("CATEGORY");
assertEquals("CATEGORY", field.getName());
}
Wir können nicht auf private Felder zugreifen, die in übergeordneten Klassen deklariert und in der untergeordneten Klasse nicht deklariert sind. Aus diesem Grund können wir nicht auf das Feldnamezugreifen.
Wir können jedoch private Felder untersuchen, die in der Klasse deklariert sind, mit der wir uns befassen, indem wir die MethodegetDeclaredFieldsaufrufen:
@Test
public void givenClass_whenGetsDeclaredFields_thenCorrect(){
Class> birdClass = Class.forName("com.example.reflection.Bird");
Field[] fields = birdClass.getDeclaredFields();
assertEquals(1, fields.length);
assertEquals("walks", fields[0].getName());
}
Wir können auch die andere Variante verwenden, wenn wir den Namen des Feldes kennen:
@Test
public void givenClass_whenGetsFieldsByName_thenCorrect() {
Class> birdClass = Class.forName("com.example.reflection.Bird");
Field field = birdClass.getDeclaredField("walks");
assertEquals("walks", field.getName());
}
Wenn der Name des Feldes falsch ist oder ein nicht vorhandenes Feld eingegeben wird, erhalten wirNoSuchFieldException.
Wir erhalten den Feldtyp wie folgt:
@Test
public void givenClassField_whenGetsType_thenCorrect() {
Field field = Class.forName("com.example.reflection.Bird")
.getDeclaredField("walks");
Class> fieldClass = field.getType();
assertEquals("boolean", fieldClass.getSimpleName());
}
Als Nächstes werden wir uns ansehen, wie auf Feldwerte zugegriffen und diese geändert werden. Um den Wert eines Feldes zu erhalten, geschweige denn es festzulegen, müssen wir zuerst festlegen, dass es zugänglich ist, indem wir die MethodesetAccessiblefür das ObjektFieldaufrufen und booleschetrue an dieses übergeben:
@Test
public void givenClassField_whenSetsAndGetsValue_thenCorrect() {
Class> birdClass = Class.forName("com.example.reflection.Bird");
Bird bird = (Bird) birdClass.newInstance();
Field field = birdClass.getDeclaredField("walks");
field.setAccessible(true);
assertFalse(field.getBoolean(bird));
assertFalse(bird.walks());
field.set(bird, true);
assertTrue(field.getBoolean(bird));
assertTrue(bird.walks());
}
Im obigen Test stellen wir fest, dass der Wert des Feldswalkstatsächlich falsch ist, bevor wir es auf true setzen.
Beachten Sie, wie wir das ObjektFieldverwenden, um Werte festzulegen und abzurufen, indem wir ihm die Instanz der Klasse übergeben, mit der wir uns befassen, und möglicherweise den neuen Wert, den das Feld in diesem Objekt haben soll.
Eine wichtige Sache, die Sie beiField Objekten beachten sollten, ist, dass wir, wenn es alspublic static deklariert ist, keine Instanz der Klasse benötigen, die sie enthält, sondern nurnull darin übergeben können Platzieren Sie den Standardwert des Felds und erhalten Sie ihn trotzdem wie folgt:
@Test
public void givenClassField_whenGetsAndSetsWithNull_thenCorrect(){
Class> birdClass = Class.forName("com.example.reflection.Bird");
Field field = birdClass.getField("CATEGORY");
field.setAccessible(true);
assertEquals("domestic", field.get(null));
}
8. Prüfmethoden
In einem vorherigen Beispiel haben wir Reflection nur zum Überprüfen von Methodennamen verwendet. Java-Reflexion ist jedoch mächtiger als das.
Mit Java Reflection können wirinvoke methods atruntime und übergeben ihnen die erforderlichen Parameter, genau wie wir es für Konstruktoren getan haben. In ähnlicher Weise können wir auch überladene Methoden aufrufen, indem wir die jeweiligen Parametertypen angeben.
Genau wie bei Feldern gibt es zwei Hauptmethoden, die wir zum Abrufen von Klassenmethoden verwenden. Die MethodegetMethods gibt ein Array aller öffentlichen Methoden der Klasse und der Superklassen zurück.
Dies bedeutet, dass wir mit dieser Methode öffentliche Methoden der Klassejava.lang.Object wietoString, hashCode undnotifyAll erhalten können:
@Test
public void givenClass_whenGetsAllPublicMethods_thenCorrect(){
Class> birdClass = Class.forName("com.example.java.reflection.Bird");
Method[] methods = birdClass.getMethods();
List methodNames = getMethodNames(methods);
assertTrue(methodNames.containsAll(Arrays
.asList("equals", "notifyAll", "hashCode",
"walks", "eats", "toString")));
}
Um nur öffentliche Methoden der Klasse zu erhalten, an der wir interessiert sind, müssen wir die MethodegetDeclaredMethodsverwenden:
@Test
public void givenClass_whenGetsOnlyDeclaredMethods_thenCorrect(){
Class> birdClass = Class.forName("com.example.java.reflection.Bird");
List actualMethodNames
= getMethodNames(birdClass.getDeclaredMethods());
List expectedMethodNames = Arrays
.asList("setWalks", "walks", "getSound", "eats");
assertEquals(expectedMethodNames.size(), actualMethodNames.size());
assertTrue(expectedMethodNames.containsAll(actualMethodNames));
assertTrue(actualMethodNames.containsAll(expectedMethodNames));
}
Jede dieser Methoden hat die singuläre Variation, die ein einzelnesMethod-Objekt zurückgibt, dessen Namen wir kennen:
@Test
public void givenMethodName_whenGetsMethod_thenCorrect() {
Class> birdClass = Class.forName("com.example.reflection.Bird");
Method walksMethod = birdClass.getDeclaredMethod("walks");
Method setWalksMethod = birdClass.getDeclaredMethod("setWalks", boolean.class);
assertFalse(walksMethod.isAccessible());
assertFalse(setWalksMethod.isAccessible());
walksMethod.setAccessible(true);
setWalksMethod.setAccessible(true);
assertTrue(walksMethod.isAccessible());
assertTrue(setWalksMethod.isAccessible());
}
Beachten Sie, wie wir einzelne Methoden abrufen und angeben, welche Parametertypen sie annehmen. Diejenigen, die keine Parametertypen annehmen, werden mit einem leeren Variablenargument abgerufen, sodass nur ein einziges Argument übrig bleibt, der Methodenname.
Als Nächstes zeigen wir, wie eine Methode zur Laufzeit aufgerufen wird. Wir wissen standardmäßig, dass daswalks-Attribut derBird-Klassefalse ist. Wir möchten diesetWalks-Methode aufrufen und auftrue setzen:
@Test
public void givenMethod_whenInvokes_thenCorrect() {
Class> birdClass = Class.forName("com.example.reflection.Bird");
Bird bird = (Bird) birdClass.newInstance();
Method setWalksMethod = birdClass.getDeclaredMethod("setWalks", boolean.class);
Method walksMethod = birdClass.getDeclaredMethod("walks");
boolean walks = (boolean) walksMethod.invoke(bird);
assertFalse(walks);
assertFalse(bird.walks());
setWalksMethod.invoke(bird, true);
boolean walks2 = (boolean) walksMethod.invoke(bird);
assertTrue(walks2);
assertTrue(bird.walks());
}
Beachten Sie, wie wir zuerst die Methodewalksaufrufen, den Rückgabetyp in den entsprechenden Datentyp umwandeln und dann seinen Wert überprüfen. Wir rufen dann später die MethodesetWalksauf, um diesen Wert zu ändern und erneut zu testen.
9. Fazit
In diesem Lernprogramm haben wir uns mit der Java Reflection-API befasst und erklärt, wie sie zur Laufzeit zum Untersuchen von Klassen, Schnittstellen, Feldern und Methoden verwendet werden kann, ohne dass die Interna zum Zeitpunkt der Kompilierung bekannt sind.
Der vollständige Quellcode und Beispiele für dieses Tutorial finden Sie in meinemGithub-Projekt.