Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage
<< | < | > | >> | API | Kapitel 43 - Reflection

## 43.4 Zugriff auf Membervariablen

- 43.4 Zugriff auf Membervariablen

Nachdem wir uns in den vorangegangenen Abschnitten die beiden Reflection-Aspekte Instanzierung und Methodenaufruf angesehen haben, wollen wir uns nun mit dem Zugriff auf Membervariablen (also auf die »Felder« eines Objekts) mit Hilfe von Reflection beschäftigen. Prinzipiell gibt es keine großen Unterschiede gegenüber dem Zugriff auf eine Methode oder einen Konstruktor:

1. Zunächst wird ein Klassenobjekt für das zu bearbeitende Objekt beschafft.
2. Dann wird eine Methode aufgerufen, um ein Bearbeitungsobjekt für eine ganz bestimmte oder eine Liste aller Membervariablen zu beschaffen.
3. Das Bearbeitungsobjekt wird verwendet, um die Membervariable zu lesen, zu verändern oder andere Eigenschaften abzufragen.

Der erste Schritt erfolgt wie gewohnt durch Aufruf von `getClass` oder die `.class`-Notation. Die im zweiten Schritt benötigten Bearbeitungsobjekte sind vom Typ `Field`, sie können auf dem Klassenobjekt durch Aufruf einer der folgenden Methoden beschafft werden:

    Field getField(String name)
    Field[] getFields()

    Field getDeclaredField(String name)
    Field[] getDeclaredFields()
| java.lang.Class

Mit `getField` wird die Membervariable mit dem angegebenen Namen beschafft, und `getFields` liefert ein Array mit `Field`-Objekten zu allen öffentlichen Membervariablen. Die beiden Methoden `getDeclaredField` und `getDeclaredFields` liefern darüber hinaus auch nicht-öffentliche Membervariablen.

Die Klasse `Field` besitzt Methoden zum Zugriff auf die Membervariable:

    Class getType()

    Object get(Object obj)

    void set(Object obj, Object value)
| java.lang.reflect.Field

Mit `getType` kann der Typ der Membervariable bestimmt werden. Das zurückgegebene Klassenobjekt beschreibt ihn in derselben Weise wie beispielsweise das Parameterobjekt von `getMethod`. Mit `get` kann auf den Wert zugegriffen werden, den die Membervariable in dem als Argument übergebenen Objekt hat. Handelt es sich um einen primitiven Typ, wird dieser automatisch in die passende Wrapper-Klasse verpackt und als Objekt zurückgegeben. Referenztypen werden unverändert zurückgegeben. Mit Hilfe der Methode `set` kann der Wert einer Membervariable verändert werden. Das erste Argument repräsentiert das zu veränderende Objekt und das zweite den neuen Wert der Membervariable. Soll ein primitiver Typ verändert werden, muß er vor der Übergabe in die passende Wrapper-Klasse verpackt werden.

|  |  |  | Neben den generischen `get`- und `set`-Methoden gibt es diese auch in typisierter Form. So dient beispielsweise `getInt` dazu, ein `int`-Feld abzufragen, mit `getDouble` kann auf ein `double` zugegriffen werden usw. Das Gegenstück dazu sind die Methoden `setInt`, `setDouble` usw., mit denen die Membervariablen typisiert verändert werden können. |  |
Tip

Wir wollen als Beispiel eine Klasse `PrintableObject` erstellen, die direkt aus `Object` abgeleitet ist und die Methode `toString` überlagert. Im Gegensatz zur Implementierung von `Object` soll unsere Variante von `toString` in der Lage sein, die Namen und Inhalte aller Membervariablen des zugehörigen Objekts auszugeben:

    001 /* PrintableObject.java */
    002
    003 import java.lang.reflect.*;
    004
    005 public class PrintableObject
    006 {
    007   public String toString()
    008   {
    009     StringBuffer sb = new StringBuffer(200);
    010     Class clazz = getClass();
    011     while (clazz != null) {
    012       Field[] fields = clazz.getDeclaredFields();
    013       for (int i = 0; i < fields.length; ++i) {
    014         sb.append(fields[i].getName() + " = ");
    015         try {
    016           Object obj = fields[i].get(this);
    017           if (obj.getClass().isArray()) {
    018             Object[] ar = (Object[])obj;
    019             for (int j = 0; j < ar.length; ++j) {
    020               sb.append(ar[j].toString() + " ");
    021             }
    022             sb.append("\n");
    023           } else {
    024             sb.append(obj.toString() + "\n");
    025           }
    026         } catch (IllegalAccessException e) {
    027           sb.append(e.toString() + "\n");
    028         }
    029       }
    030       clazz = clazz.getSuperclass();
    031     }
    032     return sb.toString();
    033   }
    034
    035   public static void main(String[] args)
    036   {
    037     JavaProgrammer jim = new JavaProgrammer();
    038     jim.name           = "Jim Miller";
    039     jim.department     = "Operating Systems";
    040     jim.age            = 32;
    041     String[] langs     = {"C", "Pascal", "PERL", "Java"};
    042     jim.languages      = langs;
    043     jim.linesofcode    = 55000;
    044     jim.jdk12          = true;
    045     jim.swing          = false;
    046     System.out.println(jim);
    047   }
    048 }
    049
    050 class Employee
    051 extends PrintableObject
    052 {
    053   public String name;
    054   public String department;
    055   public int    age;
    056 }
    057
    058 class Programmer
    059 extends Employee
    060 {
    061   public String[] languages;
    062   public int      linesofcode;
    063 }
    064
    065 class JavaProgrammer
    066 extends Programmer
    067 {
    068   public boolean jdk12;
    069   public boolean swing;
    070 }
| PrintableObject.java

Listing 43.7: Die Klasse PrintableObject

`toString` besorgt zunächst ein Klassenobjekt zum aktuellen Objekt. Mit `getDeclaredFields` wird dann eine Liste aller Felder (nicht nur der öffentlichen) dieser Klasse besorgt und jeweils mit `getName` sein Name und mit `get` sein Wert ausgegeben. Wir machen uns dabei die Fähigkeit zunutze, daß alle Objekte (auch die Wrapper-Klassen der primitiven Typen) eine Methode `toString` haben, die ihren Wert als String liefert. Ist die Membervariable ein Array, durchläuft das Programm zusätzlich seine Elemente und gibt sie einzeln aus.

Da eine Klasse Membervariablen aus ihren Vaterklassen erbt, ist es notwendig, die Vererbungshierarchie von unten nach oben zu durchlaufen, denn `getDeclaredFields` liefert nur die Membervariablen der aktuellen Klasse. Der Aufruf von `getSuperClass` am Ende der Schleife liefert zur aktuellen Klasse die Vaterklasse. Ist der Rückgabewert `null`, so ist das Ende der Vererbungshierarchie erreicht (`clazz` repräsentiert dann die Klasse `Object`) und die Schleife wird beendet.

Die Klassen `Employee`, `Programmer` und `JavaProgrammer` zeigen beispielhaft die Anwendung von `PrintableObject`. `Employee` ist aus `PrintableObject` abgeleitet und erbt die modifizierte Methode `toString`. `Programmer` ist aus `Employee` abgeleitet und `JavaProgrammer` aus `Programmer`. Das Hauptprogramm erzeugt ein Objekt des Typs `JavaProgrammer` und weist seinen eigenen und den geerbten Membervariablen Werte zu, die durch den anschließenden Aufruf von `toString` auf dem Bildschirm ausgegeben werden. Die Ausgabe des Programms ist:

    jdk12 = true
    swing = false
    languages = C Pascal PERL Java
    linesofcode = 55000
    name = Jim Miller
    department = Operating Systems
    age = 32

Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0
<< | < | > | >> | API | © 1998-2002 Guido Krüger, http://www.javabuch.de