Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.
 
Inhaltsverzeichnis
Vorwort
1 Neues in Java 8 und Java 7
2 Fortgeschrittene String-Verarbeitung
3 Threads und nebenläufige Programmierung
4 Datenstrukturen und Algorithmen
5 Raum und Zeit
6 Dateien, Verzeichnisse und Dateizugriffe
7 Datenströme
8 Die eXtensible Markup Language (XML)
9 Dateiformate
10 Grafische Oberflächen mit Swing
11 Grafikprogrammierung
12 JavaFX
13 Netzwerkprogrammierung
14 Verteilte Programmierung mit RMI
15 RESTful und SOAP-Web-Services
16 Technologien für die Infrastruktur
17 Typen, Reflection und Annotationen
18 Dynamische Übersetzung und Skriptsprachen
19 Logging und Monitoring
20 Sicherheitskonzepte
21 Datenbankmanagement mit JDBC
22 Java Native Interface (JNI)
23 Dienstprogramme für die Java-Umgebung
Stichwortverzeichnis

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Java SE 8 Standard-Bibliothek von Christian Ullenboom
Das Handbuch für Java-Entwickler
Buch: Java SE 8 Standard-Bibliothek

Java SE 8 Standard-Bibliothek
Pfeil 17 Typen, Reflection und Annotationen
Pfeil 17.1 Metadaten
Pfeil 17.1.1 Metadaten durch Javadoc-Tags
Pfeil 17.2 Metadaten der Typen mit dem Class-Objekt
Pfeil 17.2.1 An ein Class-Objekt kommen
Pfeil 17.2.2 Eine Class ist ein Type
Pfeil 17.3 Klassenlader
Pfeil 17.3.1 Das Verzeichnis jre/lib/endorsed *
Pfeil 17.3.2 Die Klasse java.lang.ClassLoader
Pfeil 17.3.3 Hot Deployment mit dem URL-Classloader *
Pfeil 17.4 Metadaten der Typen mit dem Class-Objekt
Pfeil 17.4.1 Der Name des Typs
Pfeil 17.4.2 Was das Class-Objekt beschreibt *
Pfeil 17.4.3 instanceof mit Class-Objekten *
Pfeil 17.4.4 Oberklassen finden *
Pfeil 17.4.5 Implementierte Interfaces einer Klasse oder eines Interfaces *
Pfeil 17.4.6 Modifizierer und die Klasse Modifier *
Pfeil 17.4.7 Die Arbeit auf dem Feld *
Pfeil 17.5 Attribute, Methoden und Konstruktoren
Pfeil 17.5.1 Reflections – Gespür für die Attribute einer Klasse
Pfeil 17.5.2 Schnittstelle Member für Eigenschaften
Pfeil 17.5.3 Field-Klasse
Pfeil 17.5.4 Methoden einer Klasse erfragen
Pfeil 17.5.5 Properties einer Bean erfragen
Pfeil 17.5.6 Konstruktoren einer Klasse
Pfeil 17.5.7 Annotationen
Pfeil 17.6 Objekte erzeugen und manipulieren
Pfeil 17.6.1 Objekte erzeugen
Pfeil 17.6.2 Die Belegung der Variablen erfragen
Pfeil 17.6.3 Eine generische eigene toString()-Methode *
Pfeil 17.6.4 Variablen setzen
Pfeil 17.6.5 Bean-Zustände kopieren *
Pfeil 17.6.6 Private Attribute ändern
Pfeil 17.6.7 Methoden aufrufen
Pfeil 17.6.8 Statische Methoden aufrufen
Pfeil 17.6.9 Dynamische Methodenaufrufe bei festen Methoden beschleunigen *
Pfeil 17.6.10 java.lang.reflect.Parameter
Pfeil 17.7 Eigene Annotationstypen *
Pfeil 17.7.1 Annotationen zum Laden von Ressourcen
Pfeil 17.7.2 Neue Annotationen deklarieren
Pfeil 17.7.3 Annotationen mit genau einem Attribut
Pfeil 17.7.4 Element-Wert-Paare (Attribute) hinzufügen
Pfeil 17.7.5 Annotationsattribute vom Typ einer Aufzählung
Pfeil 17.7.6 Felder von Annotationsattributen
Pfeil 17.7.7 Vorbelegte Attribute
Pfeil 17.7.8 Annotieren von Annotationstypen
Pfeil 17.7.9 Deklarationen für unsere Ressourcen-Annotationen
Pfeil 17.7.10 Annotierte Elemente auslesen
Pfeil 17.7.11 Auf die Annotationsattribute zugreifen
Pfeil 17.7.12 Komplettbeispiel zum Initialisieren von Ressourcen
Pfeil 17.7.13 Mögliche Nachteile von Annotationen
Pfeil 17.8 Zum Weiterlesen
 
Zum Seitenanfang

17.6Objekte erzeugen und manipulieren Zur vorigen ÜberschriftZur nächsten Überschrift

Nachdem wir nun genügend über das Ausfragen von Klassen-, Variablen-, Methoden- und Konstruktor-Objekten wissen, wollen wir aktiv eigene Objekte erzeugen, Werte von Variablen abfragen und verändern sowie Methoden dynamisch per Reflection aufrufen.

 
Zum Seitenanfang

17.6.1Objekte erzeugen Zur vorigen ÜberschriftZur nächsten Überschrift

Der new-Operator erzeugt in Java zur Laufzeit ein Exemplar einer Klasse. Der Compiler muss dazu den Namen der Klasse kennen, sodass er einen passenden Konstruktor-Aufruf erzeugen kann. Kennen wir aber erst später zur Laufzeit den Namen der gewünschten Klasse für unser Objekt, so fällt die new-Operation flach, weil der new-Operator nicht für diesen Spezialfall gedacht ist.

Um Exemplare bestimmter Klassen dynamisch zu erzeugen, brauchen wir wieder ein passendes Class-Objekt. Ab hier gibt es zwei Wege.

newInstance() von Class

Der einfachste Weg führt über die newInstance()-Methode vom Class-Objekt.

final class java.lang.Class<T>
implements Serializable, GenericDeclaration, Type, AnnotatedElement
  • T newInstance()throws InstantiationException,IllegalAccessException
    Erzeugt ein neues Exemplar über den Standard-Konstruktor.

Mit dieser Variante lassen sich jedoch keine parametrisierten Konstruktoren aufrufen.

newInstance() von Constructor

Holen wir uns vom Class-Objekt mit getConstructor() ein Konstruktor-Objekt, das den gewünschten Konstruktor beschreibt. Jedes Konstruktor-Objekt kennt eine newInstance(Object... initargs)-Methode, die ein neues Exemplar erschafft, indem sie den zugrunde liegenden Konstruktor aufruft. Der Parameter von newInstance(…) ist ein Vararg von Werten, die an den echten Konstruktor gehen. Bei einem parameterlosen Konstruktor können wir also einfach newInstance() aufrufen.

[zB]Beispiel

Ein Reflection-Konstruktor erzeugt ein Point-Objekt mit den Koordinaten 10, 20:

Listing 17.13com/tutego/insel/meta/CreateObject.java, main()

Class<Point> pointClass = Point.class;
Constructor<Point> constructor =
pointClass.getConstructor( int.class, int.class );
Point p = constructor.newInstance( 10, 20 );
System.out.println( p );
final class java.lang.Class<T>
implements Serializable, GenericDeclaration, Type, AnnotatedElement
  • Constructor<T> getConstructor(Class... parameterTypes)
      throws NoSuchMethodException
    Liefert den sichtbaren Konstruktor mit dem gewünschten Typ.

final class java.lang.reflect.Constructor<T>
extends Executable
  • T newInstance(Object... initargs)
      throws InstantiationException, IllegalAccessException,
      IllegalArgumentException, InvocationTargetException
    Erzeugt ein neues Exemplar, indem es den durch das Constructor-Objekt repräsentierten Konstruktor mit den im Array angegebenen Parametern aufruft. Auf einige Exceptions ist zu achten:

    • IllegalAccessException: Auf den Konstruktor kann nicht zugegriffen werden (zum Beispiel, weil er privat ist).

    • IllegalArgumentException: Die Anzahl der Parameter ist falsch bzw. eine Konvertierung der Parameterwerte in die benötigten Typen ist nicht möglich.

    • InstantiationException: Das Constructor-Objekt bezieht sich auf einen Konstruktor einer abstrakten Klasse.

InvocationTargetException

Die Ausnahme InvocationTargetException ist keine RuntimeException und kommt bei zwei Methoden aus der Reflection-API vor:

  • newInstance(…) zum Erzeugen von Objekten bei Constructor

  • invoke(…) zum Aufruf von Methoden bei Method

Die InvocationTargetException ist ein Mantel um genau die Ausnahme, die der aufgerufene Konstruktor bzw. die Methode ausgelöst hat. Lösen der Konstruktor oder die Methode eine Ausnahme aus, so generiert die JVM eine InvocationTargetException und lässt uns mit getCause() bzw. mit getTargetException() den Grund als Throwable erfragen.

 
Zum Seitenanfang

17.6.2Die Belegung der Variablen erfragen Zur vorigen ÜberschriftZur nächsten Überschrift

Schreiben wir einen GUI-Builder oder einen Debugger, so reicht es nicht aus, nur die Namen und Datentypen der Variablen zu kennen. Wir wollen auch auf ihre Inhalte lesend und schreibend zugreifen. Das ist mithilfe der verschiedenen getXXX()-Methoden für ein Field-Objekt leicht machbar. Der erste Schritt besteht also wieder darin, ein Class-Objekt zu erfragen. Dann besorgen wir uns mittels getFields() ein Array von Attributbeschreibungen – oder mit getField(String) ein spezielles Attribut – und nehmen das Field-Objekt für unsere gewünschte Variable. Den Wert, den das Field dann speichert, erfragt die Methode get(Object). Sie ist auf allen Variablentypen möglich und konvertiert automatisch in Wrapper-Objekte, wenn der Typ primitiv war. Die Field-Klasse bietet einige spezielle getXXX(Object)-Methoden, um besonders einfach an die Werte von Variablen primitiven Typs zu gelangen. So liefert getDouble(Object) einen double-Wert und getInt(Object) ein int. Wir müssen daran denken, dass IllegalArgumentException und IllegalAccessException bei falschem Zugriff auftreten können.

Alle getXXX(Object)-Methoden zum Erfragen – und auch setXXX(Object, …)-Methoden zum Setzen – erwarten ein Argument mit dem Verweis auf das Objekt, das die Variable besitzt. Das Argument wird ignoriert (und sollte null sein), wenn es sich um eine statische Variable handelt.

Rechtecke erzeugen und erfragen

Ein Programm soll ein Rectangle-Objekt mit einer Belegung für x, y, Höhe und Breite erzeugen. Anschließend erfragt getField(String) das Field-Objekt für eine Beschreibung der Variablen mit dem gegebenen Namen. Das Field-Objekt gibt mit getXXX(…) den Inhalt der Variablen preis. Um das Prinzip zu demonstrieren, erfragt die get(Object)-Methode die Höhe height, die ein Integer-Objekt zurückgibt. Alle anderen Ganzzahlwerte liefert die spezialisierte Helfermethode getInt(Object):

Listing 17.14com/tutego/insel/meta/GetFieldElements.java

package com.tutego.insel.meta;

import java.lang.reflect.*;

class GetFieldElements {
public static void main( String[] args ) {
Object o = new java.awt.Rectangle( 11, 22, 33, 44 );
Class<?> c = o.getClass();

try {
Field heightField = c.getField( "height" ),
widthField = c.getField( "width" ),
xField = c.getField( "x" ),
yField = c.getField( "y" );

Integer height = (Integer) heightField.get( o );

int width = widthField.getInt( o ),
x = xField.getInt( o ),
y = yField.getInt( o );

String s = c.getName() + "[x=" + x + ",y=" + y +
",width=" + width + ",height=" + height + "]";

System.out.println( s ); // java.awt.Rectangle[x=11,y=22,width=33,height=44]
System.out.println( o ); // java.awt.Rectangle[x=11,y=22,width=33,height=44]
}
catch ( ReflectiveOperationException e ) {
e.printStackTrace();
}
}
}

Es erzeugt nun nach dem Aufruf die Ausgabe:

java.awt.Rectangle[x=11,y=22,width=33,height=44]
java.awt.Rectangle[x=11,y=22,width=33,height=44]
final class java.lang.reflect.Field
extends AccessibleObject
implements Member
  • String getName()
    Liefert den Namen der Variablen. Diese Methode ist Teil der Schnittstelle Member.

  • int getModifiers()
    Liefert die Modifizierer. Diese Methode ist Teil der Schnittstelle Member.

  • Object get(Object obj)

  • boolean getBoolean(Object obj)

  • byte getByte(Object obj)

  • char getChar(Object obj)

  • double getDouble(Object obj)

  • float getFloat(Object obj)

  • int getInt(Object obj)

  • long getLong(Object obj)

  • short getShort(Object obj)
    Erfragt den Wert eines Attributs. Die Referenz von obj zeigt auf das Objekt, das das Attribut enthält. Es sollte null übergeben werden, wenn es sich um eine statische Variable handelt.

 
Zum Seitenanfang

17.6.3Eine generische eigene toString()-Methode * Zur vorigen ÜberschriftZur nächsten Überschrift

Die toString()-Methode ist für viele Klassen nicht überlebenswichtig, aber außerordentlich praktisch. Es ist viel zu schade, hier Zeit für die Entwicklung zu investieren. Toll wäre es, wenn toString() ganz automatisch die Attribute und Belegungen analysierte und ausgäbe. Aber wer Allgemeines sucht, findet die Antwort in Reflection. Wir schreiben einfach eine statische Methode toString(Object) in eine Hilfsklasse und erfragen dann alle Attribute und die Werte des zu untersuchenden Objekts. Etwas Programmieraufwand müssen wir noch in die Behandlung der Oberklasse investieren, denn Reflection auf einem Objekt zeigt nur die in der Klasse deklarierten Attribute, nicht aber die geerbten Attribute an. Die Lösung ergibt sich fast von selbst:

Listing 17.15com/tutego/insel/meta/ToStringHelper.java

package com.tutego.insel.meta;

import java.lang.reflect.*;
import java.util.*;

public class ToStringHelper {
public static String toString( Object o ) {
Collection<String> result = new ArrayList<>();

toString( o, o.getClass(), result );

return o.getClass().getName().concat( result.toString() );
}

private static void toString( Object o, Class<?> clazz,
Collection<String> list ) {
Field[] fields = clazz.getDeclaredFields();
AccessibleObject.setAccessible( fields, true );

for ( Field f : fields ) {
try {
list.add( f.getName() + "=" + f.get( o ) );
}
catch ( IllegalAccessException e ) {
e.printStackTrace();
}
}
if ( clazz.getSuperclass().getSuperclass() != null )
toString( o, clazz.getSuperclass(), list );
}
}

Die private Methode toString(Object, Class, ArrayList) dient eigentlich nur dem rekursiven Aufruf durch die Oberklassen. Falls es eine Oberklasse gibt, also clazz.getSuperclass().getSuperclass() ein Objekt liefert, müssen wir für die Oberklasse ebenfalls die Attribute ablaufen. Das machen wir rekursiv.

Testen wir anschließend ToStringHelper in einer Klasse ToStringHelperDemo, die von Ober abgeleitet ist. Damit bekommen wir zwei Attribute in der Oberklasse. Eines davon ist interessant (die Variable i), denn die Unterklasse überdeckt sie. Dennoch findet unser toString(Object) beide Belegungen. Wäre das nicht erwünscht, müssten wir einfach die Liste durchschauen und suchen, ob schon ein Attribut mit dem gleichen Namen vorhanden ist. Da der Algorithmus rekursiv erst die Unterklasse und dann die Oberklasse(n) durchsucht, bekommen wir auch die Attribute in dem sichtbaren Bereich, wie sie auch der Benutzer sieht:

Listing 17.16com/tutego/insel/meta/ToStringHelperDemo.java

package com.tutego.insel.meta;

class Ober {
int i = 123;
/* private */double d = 3.1415;
}

public class ToStringHelperDemo extends Ober {
String hello = "world";
int i = 42;

public static void main( String[] args ) {
ToStringHelperDemo t = new ToStringHelperDemo();

System.out.println( ToStringHelper.toString( t ) );
// com.tutego.insel.meta.ToStringHelperDemo[hello=world, i=42, i=123, d=3.1415]
}
}
 
Zum Seitenanfang

17.6.4Variablen setzen Zur vorigen ÜberschriftZur nächsten Überschrift

Bei Debuggern oder grafischen Editoren ist es nur eine Seite der Medaille, die Werte von Variablen anzuzeigen. Hinzu kommt noch das Setzen der Werte von Variablen. Dies ist aber genauso einfach wie das Abfragen. An Stelle der getXXX(…)-Methoden kommen nun verschiedene setXXX(…)-Methoden zum Einsatz. So trägt setBoolean(…) einen Wahrheitswert oder setDouble(…) eine Fließkommazahl in eine Variable ein. Eine allgemeine set(Object obj, Object value)-Methode dient Objektreferenzen wie im Fall von get(…). Die Methode set(…) nimmt ebenso Wrapper-Objekte für Variablen von primitiven Datentypen. Die folgenden set<Typ>(Object obj, …)-Methoden setzen daher alle »ihren« Datentyp. Wir müssen aber dafür sorgen, dass die Variable existiert und wir Zugriff darauf haben. In allen Fällen muss auf IllegalArgumentException und IllegalAccessException geachtet werden.

Das folgende Programm erzeugt klassisch ein Point-Objekt mit dem Konstruktor, der x und y setzt. Anschließend verändert die eigene Methode modify(…) ein gewünschtes Attribut:

Listing 17.17com/tutego/insel/meta/SetFieldElements.java

package com.tutego.insel.meta;

import java.lang.reflect.*;
import java.awt.*;

class SetFieldElements {
public static void main( String[] args ) {
Point p = new Point( 11, 22 );
System.out.println( p );

modify( p, "x", 1111 ); // java.awt.Point[x=11,y=22]
modify( p, "y", 2222 ); // java.awt.Point[x=1111,y=2222]

System.out.println( p );

modify( p, "z", 0 ); // java.lang.NoSuchFieldException: z
}

static void modify( Object o, String name, Integer param ) {
try {
Field field = o.getClass().getField( name );
field.set( o, param );
}
catch ( ReflectiveOperationException e ) {
e.printStackTrace();
}
}
}

Die Veränderung der Variablen erfolgt mit der set(Object, Object)-Methode. Da wir primitive Datentypen übergeben, wickeln wir sie für die modify(…)-Methode in ein Integer-Objekt ein. Das Integer-Objekt haben wir nicht explizit programmiert, denn es wird über Boxing vom Compiler selbst zur Verfügung gestellt. Für bekannte Typen könnten wir neben der allgemeinen Methode set(…) auch etwa setInt(…) verwenden.

final class java.lang.reflect.Field
extends AccessibleObject
implements Member
  • void set(Object obj, Object value)
    Setzt das Attribut des Objekts obj, das dieses Field-Objekt repräsentiert, auf den neuen Wert value.

  • void setBoolean(Object obj, boolean z)

  • void setByte(Object obj, byte b)

  • void setChar(Object obj, char c)

  • void setDouble(Object obj, double d)

  • void setFloat(Object obj, float f)

  • void setInt(Object obj, int i)

  • void setLong(Object obj, long l)

  • void setShort(Object obj, short s)
    Belegt das Feld eines Objekts obj mit einem primitiven Element.

 
Zum Seitenanfang

17.6.5Bean-Zustände kopieren * Zur vorigen ÜberschriftZur nächsten Überschrift

In mehrschichtigen Anwendungen gibt es oft das Muster, dass eine JavaBean etwa über eine objektrelationale Mapping-Technologie automatisch aus einer Datenbankzeile aufgebaut wird und dann intern in der Geschäftsschicht verwendet wird. Soll nun diese Information über das Netzwerk an einen anderen Rechner verteilt werden, ist es nicht immer angebracht, diese JavaBean etwa direkt über Serialisierung zu versenden. Stattdessen kann ein Transfer-Objekt aufgebaut werden, eine spezielle JavaBean zum Beispiel, sodass der Empfänger keine Abhängigkeit zu der Bean in der internen Geschäftsschicht hat. Nun werden sich aber diese Geschäftsschicht-Bean und die Transfer-Bean sehr ähnlich sein, und viele Entwickler scheuen die Mühe, lästigen Kopiercode zu erstellen. Doch manuelle Arbeit ist nicht nötig, und eine Lösung für das Kopierproblem ist über Reflection schnell geschrieben. Über die BeanInfo kommen wir an den PropertyDescriptor (siehe dazu Abschnitt 17.5.5, »Properties einer Bean erfragen«), und dann liefern getReadMethod() und getWriteMethod() die Setter/Getter. Bei einer eigenen Kopiermethode wie copyProperties(Object source, Object target) müssen wir bei der Quell-Bean jede Property auslesen und entsprechend bei der Ziel-Bean nach der Property suchen und den Setter aufrufen. Wenn das Ganze ohne Typkonvertierungen programmiert werden soll, sind es nur wenige Zeilen Programmcode. Kommen einfache Konvertierungen dazu, etwa wenn einmal ein Wrapper als Property-Typ genutzt wird und einmal der primitive Datentyp, ist es etwas mehr.

Der Aufwand mit einer eigenen Implementierung ist allerdings nicht nötig, denn zwei populäre Implementierungen können helfen:

  • Apache Commons BeanUtils (http://commons.apache.org/proper/commons-beanutils/):
    Die Klasse org.apache.commons.beanutils.BeanUtils bietet praktische statische Methoden wie copyProperty(Object bean, String name, Object value), copyProperties(Object dest, Object orig), Object cloneBean(Object bean) oder populate(Object bean, Map properties).

  • Dozer (http://dozer.sourceforge.net/): Dozer bringt ausgefeilte Mapping-Möglichkeiten mit, die weit über BeansUtils hinausgehen. Das geht so weit, dass es ein Eclipse-Plugin zur Konfiguration der Abbildungen gibt.

 
Zum Seitenanfang

17.6.6Private Attribute ändern Zur vorigen ÜberschriftZur nächsten Überschrift

Wenn es der Sicherheitsmanager zulässt, kann ein Programm auch private- oder protected-Attribute ändern und Methoden/Konstruktoren eingeschränkter Sichtbarkeit aufrufen. Die Schlüsselfigur in diesem Spiel ist die Oberklasse java.lang.reflect.AccessibleObject, die den Klassen Field und Excecutable (und damit Constructor und Method) die Methode setAccessible(boolean) vererbt. Ist das Argument true und lässt der Sicherheitsmanager die Operation zu, lässt sich auf jedes Element (also Konstruktor, Attribut oder Methode) ungleich der Sichtbarkeitseinstellungen zugreifen:

Listing 17.18com/tutego/insel/meta/ReadPrivate.java

package com.tutego.insel.meta;

public class ReadPrivate {

@SuppressWarnings( "unused" )
private String privateKey = "Schnuppelhase";

public static void main( String[] args ) throws Exception {
ReadPrivate key = new ReadPrivate();
Class<?> c = key.getClass();
java.lang.reflect.Field field = c.getDeclaredField( "privateKey" );
field.setAccessible( true );
System.out.println( field.get(key) ); // Schnuppelhase
field.set( key, "Schnuckibutzihasidrachelchen");
System.out.println( field.get(key) ); // Schnuckibutzihasidrachelchen
}
}

[ ! ]Warnung

Mit dieser Technik lässt sich viel Unsinn anrichten. Es gibt Dinge, die in der Laufzeitumgebung einfach fest sein müssen. Dazu zählen einmal angelegte Strings oder Wrapper-Objekte. Strings sind immutable, weil sie intern in einem privaten char-Feld gehalten werden und es keine Modifikationsmöglichkeiten gibt. Auch Wrapper-Objekte sind, wenn sie einmal mit einem Konstruktor angelegt wurden, nicht über öffentliche Methoden veränderbar. Sie anschließend per Reflection zu modifizieren, bringt große Unordnung, insbesondere bei den gecachten Integer/Long-Wrapper-Objekten, die die statischen valueOf(…)-Methoden liefern.

 
Zum Seitenanfang

17.6.7Methoden aufrufen Zur vorigen ÜberschriftZur nächsten Überschrift

Nach dem Abfragen und Setzen von Variablenwerten und Konstruktor-Aufrufen zum Erzeugen eines Objekts ist das Aufrufen von Methoden per Reflection der letzte Schritt. Wenn zur Compilezeit der Name der Methode nicht feststeht, lässt sich zur Laufzeit dennoch eine im Programm deklarierte Methode aufrufen, wenn ihr Name als Zeichenkette vorliegt.

Zunächst gehen wir wieder von einem Class-Objekt aus, das die Klasse des Objekts beschreibt, für das eine Objektmethode aufgerufen werden soll. Anschließend wird ein Method-Objekt als Beschreibung der gewünschten Methode benötigt; wir bekommen dies mit der Methode getMethod(String name, Class<?>... parameterTypes) aus dem Class-Exemplar. getMethod(…) verlangt zwei Argumente: einen String mit dem Namen der Methode und ein Array von Class-Objekten. Jedes Element dieses Arrays entspricht einem Parametertyp aus der Signatur der Methode. Damit werden überladene Methoden unterschieden. Nachdem wir das beschreibende Method-Exemplar und die Parameterwerte für den Aufruf vorbereitet haben, ruft invoke(…) die Zielmethode auf – im Englischen heißt dies dynamic invocation. invoke(…) erwartet zwei Argumente: ein Array mit Argumenten, die der aufgerufenen Methode übergeben werden, und eine Referenz auf das Objekt, auf dem die Methode aufgerufen werden soll und das der Auflösung der dynamischen Bindung dient.

final class java.lang.reflect.Method
extends Executable
  • Object invoke(Object obj, Object... args)
    throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
    Ruft eine Methode des Objekts obj mit den gegebenen Argumenten auf. Wie schon beim Konstruktor löst die Methode eine InvocationTargetException aus, wenn die aufzurufende Methode eine Exception auslöst.

[zB]Beispiel

Wir erzeugen ein Point-Objekt und setzen im Konstruktor den x-Wert auf 10. Anschließend fragen wir mit der Methode getX(), die wir dynamisch aufrufen, den x-Wert wieder ab:

Listing 17.19com/tutego/insel/meta/InvokeMethod.java, main()

Point p = new Point( 10, 0 );
Method method = p.getClass().getMethod( "getX" );
String returnType = method.getReturnType().getName();
Object returnValue = method.invoke( p );
System.out.printf( "(%s) %s", returnType, returnValue ); // (double) 10.0

Beispiele der Varargs sind bei getMethod(String name, Class<?>... parameterTypes) die Parametertypen und bei invoke(Object obj, Object... args) die Argumente für setLocation(…). Da getMethod(…) eine beliebige Anzahl von Argumenten annehmen kann und kein Argument dazuzählt, muss die Methode nicht so parametrisiert werden:

Method method = p.getClass().getMethod( "getX", (Class[]) null );

Auffälliger ist die Möglichkeit der variablen Argumentanzahl bei invoke(…). Da ein Getter keine Parameter besitzt, heißt es kurz method.invoke(p), Folgendes ist nicht nötig:

method.invoke( p, (Object[]) null );

Interessant sind Methoden mit Parameterliste, wie setLocation(int, int):

Point p = new Point();
Method method = p.getClass().getMethod( "setLocation", int.class, int.class );
method.invoke( p, 1, 2 );
System.out.println( p );
 
Zum Seitenanfang

17.6.8Statische Methoden aufrufen Zur vorigen ÜberschriftZur nächsten Überschrift

Wir programmieren ein Beispiel in dem die Klasse InvokeMain die statische main(String[])-Methode einer anderen Klasse, HasMain, mit einem Parameter aufruft:

Listing 17.20com/tutego/insel/meta/InvokeMain.java

package com.tutego.insel.meta;

import java.lang.reflect.*;
import java.util.Arrays;

public class InvokeMain {
public static void main( String[] args ) throws Exception {
String[] argv = { "-option", "Parameter" };

Method method = Class.forName( "com.tutego.insel.meta.HasMain" ).
getMethod( "main", argv.getClass() );

method.invoke( null, new Object[]{ argv } );
}
}

class HasMain {
public static void main( String[] args ) {
System.out.println( "Got: " + Arrays.toString( args ) );
}
}
 
Zum Seitenanfang

17.6.9Dynamische Methodenaufrufe bei festen Methoden beschleunigen * Zur vorigen ÜberschriftZur nächsten Überschrift

Werden über Reflection Methoden aufgerufen, deren Methodennamen erst zur Laufzeit bestimmt werden, so verlieren wir die Typsicherheit vom Compiler, und die Geschwindigkeit ist vermindert. Diese Aufrufe lassen sich prinzipbedingt auch durch einen JIT-Compiler nicht weiter beschleunigen.[ 134 ](Wohl aber lässt sich über invokedynamic wieder mehr Performance gewinnen, doch das ist eine eigene Geschichte für Compilerbauer.) Wir müssen also nach einer Lösung suchen, mit der wir diese Art von Aufruf beschleunigen können. Ein möglicher Weg hierbei ist, in Kenntnis des Namens der Methode – nennen wir die Methode meth() – den Namen in einer (abstrakten) Oberklasse dem Compiler bereits bekannt zu machen. Reflection ist nur zum Laden einer Unterklasse mit gegebenem Namen nötig; die normale, dynamische Methodenbindung erledigt den Rest – ganz ohne versteckte Schnüre, doppelte Böden oder Spiegel (Reflection).

Versuchen wir, den folgenden Code nach diesem Schema zu ändern:

Listing 17.21com/tutego/insel/meta/DynamReflection.java, main()

Class<?> clazz = Class.forName("com.tutego.insel.meta.DynamReflectionMethod" );
Object o = clazz.newInstance();
clazz.getMethod( "meth" ).invoke( o );

DynamReflection ist die Hauptklasse, die die Klasse DynamReflectionMethod über Class.forName(String) anfordert:

Listing 17.22com/tutego/insel/meta/DynamReflectionMethod.java

package com.tutego.insel.meta;

public class DynamReflectionMethod {
@Override public void meth() {
System.out.println( "Bewusste Raucher trinken Filterkaffee" );
}
}

Über das Class-Objekt erzeugt newInstance() ein neues Exemplar. getMethod("meth") sucht die Beschreibung der Methode meth() heraus, und invoke(…) ruft die Methode meth() auf. Hier genau entsteht ein kleiner Geschwindigkeitsverlust. Wenn es uns gelänge, um das invoke(…) herumzukommen, wäre das schon ein großer Fortschritt. Dies schaffen wir, indem wir eine Schnittstelle (oder Oberklasse) für DynamReflectionMethod konstruieren, die genau diese Methode vorschreibt. Die implementierende Klasse (bzw. Unterklasse) wird dann eine Implementierung angeben:

Listing 17.23com/tutego/insel/meta/DynamAbstract.java

package com.tutego.insel.meta;

interface DynamBase {
void meth();
}

class DynamBaseMethod implements DynamBase {
public void meth() {
System.out.println( "Bewusste Raucher trinken Filterkaffee" );
}
}

public class DynamAbstract {
public static void main( String[] args ) throws Exception {
Class<?> clazz = Class.forName( "com.tutego.insel.meta.DynamBaseMethod" );
DynamBase o = (DynamBase) clazz.newInstance();
o.meth();
}
}

DynamBase ist eine Schnittstelle, die zur Übersetzungszeit bekannt ist. Die virtuelle Maschine löst den Aufruf nach den üblichen Regeln der dynamischen Bindung selbst auf. Die Klasse DynamBaseMethod wird ebenfalls erst zur Laufzeit geladen. Wir verstecken hier sehr elegant den Aufwand. Wir haben die gleiche Funktionalität und Flexibilität wie im vorangegangenen Reflection-Beispiel – wenn wir die Möglichkeit, den Klassennamen durch einen String anzugeben, außer Acht lassen –, aber mit der höheren Geschwindigkeit eines konventionellen Methodenaufrufs ohne Reflection.

[»]Hinweis

Der Aufruf von Class.forName(…) liefert ein Class-Objekt, bei dem der Typ nicht bekannt ist. Daher ist unsere Variable clazz mit Class<?> deklariert. Bei newInstance() ist dann auch eine explizite Typanpassung auf den Ergebnistyp nötig, was nicht nötig wäre, wenn das Class-Objekt korrekter wäre. Um das Class-Objekt von Class.forName(…) zu spezialisieren, gibt es eine Methode asSubclass(…). Dann lässt sich statt des bekannten

Class<?> clazz = Class.forName( "DynamBaseMethod" );
DynamBase o = (DynamBase) clazz.newInstance();

Folgendes schreiben:

Class<? extends DynamBase> clazz =
Class.forName( "DynamBaseMethod" ).asSubclass( DynamBase.class );
DynamBase o = clazz.newInstance();
 
Zum Seitenanfang

17.6.10java.lang.reflect.Parameter Zur vorigen ÜberschriftZur nächsten Überschrift

Ein Parameter repräsentiert einen Parameter einer Methode oder eines Konstruktors. Zu den neuen Methoden zählen:

final class java.lang.reflect.Parameter
implements AnnotatedElement
  • String getName()

  • int getModifiers()

  • boolean isNamePresent()

  • boolean isImplicit()

  • boolean isSynthetic()

  • boolean isVarArgs()

  • Type getParameterizedType()

  • Executable getDeclaringExecutable()

  • Class<?> getType()

  • AnnotatedType getAnnotatedType()

Die in Java 8 eingeführte finale Klasse Parameter implementiert AnnotatedElement, da seit Java 8 auch Parametervariablen annotiert sein können; die Methoden sind in der oberen Aufzählung nicht noch einmal aufgeführt.

Um an einen Parameter zu gelangen, nutzen wir getParameters() auf einem Executable, also konkret Constructor, Method.

abstract class java.lang.reflect.Executable<T>
extends AccessibleObject
implements Member, GenericDeclaration
  • public Parameter[] getParameters()

Falls etwas beim Erfragen schiefgeht, gibt es eine MalformedParametersException, eine ungeprüfte Ausnahme.

[zB]Beispiel

Constructor<?>[] constructors = Point.class.getDeclaredConstructors();
for ( Constructor<?> constructor : constructors ) {
System.out.println( constructor );
for ( Parameter param : constructor.getParameters() )
System.out.printf( " %s %s%n", param.getType(), param.getName() );
}

Mit der Ausgabe (an der abzusehen ist, dass die Parameternamen für die JVM nicht bekannt sind):

public java.awt.Point(int,int)
int arg0
int arg1
public java.awt.Point(java.awt.Point)
class java.awt.Point arg0
public java.awt.Point()

 


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Java SE 8 Standard-Bibliothek Java SE 8 Standard-Bibliothek
Jetzt Buch bestellen

 Buchempfehlungen
Zum Rheinwerk-Shop: Java ist auch eine Insel
Java ist auch eine Insel


Zum Rheinwerk-Shop: Professionell entwickeln mit Java EE 8
Professionell entwickeln mit Java EE 8


Zum Rheinwerk-Shop: Besser coden
Besser coden


Zum Rheinwerk-Shop: Entwurfsmuster
Entwurfsmuster


Zum Rheinwerk-Shop: IT-Projektmanagement
IT-Projektmanagement


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2018
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 
Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern