Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

 << zurück
Java ist auch eine Insel von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 5
Java ist auch eine Insel

Java ist auch eine Insel
5., akt. und erw. Auflage
1454 S., mit CD, 49,90 Euro
Galileo Computing
ISBN 3-89842-747-1
gp Kapitel 21 Reflection und Annotationen
  gp 21.1 Metadaten
    gp 21.1.1 XDoclet
  gp 21.2 Mit dem Class-Objekt etwas über Klassen erfahren
    gp 21.2.1 An ein Class-Objekt kommen
    gp 21.2.2 Was das Class-Objekt beschreibt
    gp 21.2.3 Der Name der Klasse
    gp 21.2.4 Die Arbeit auf dem Feld
    gp 21.2.5 instanceof mit Class-Objekten
    gp 21.2.6 Oberklassen finden
    gp 21.2.7 Implementierte Interfaces einer Klasse oder eines Inferfaces
    gp 21.2.8 Modifizierer und die Klasse Modifier
    gp 21.2.9 Die Attribute einer Klasse
    gp 21.2.10 Methoden einer Klasse erfragen
    gp 21.2.11 Konstruktoren einer Klasse
    gp 21.2.12 Annotationen
  gp 21.3 Objekte manipulieren
    gp 21.3.1 Objekte erzeugen
    gp 21.3.2 Die Belegung der Variablen erfragen
    gp 21.3.3 Eine generische toString()-Funktion
    gp 21.3.4 Variablen setzen
    gp 21.3.5 Private Attribute ändern
  gp 21.4 Methoden aufrufen
    gp 21.4.1 Statische Methoden aufrufen
    gp 21.4.2 Dynamische Methodenaufrufe bei festen Methoden beschleunigen
  gp 21.5 Informationen und Identifizierung von Paketen
    gp 21.5.1 Geladene Pakete
  gp 21.6 Annotationen
    gp 21.6.1 Neue Annotationen definieren
    gp 21.6.2 Annotationen mit genau einem Element
    gp 21.6.3 Beliebige Schlüssel-/Werte-Paare
    gp 21.6.4 Vorbelegte Elemente
    gp 21.6.5 Annotieren von Annotations-Typen
    gp 21.6.6 Annotationen zur Laufzeit ausgelesen
    gp 21.6.7 Mögliche Nachteile von Annotationen


Galileo Computing

21.4 Methoden aufrufedowntop

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 Compile-Zeit der Name der Methode nicht feststeht, lässt sich zur Laufzeit dennoch eine im Programm definierte Methode aufrufen, wenn ihr Name als Zeichenkette vorliegt. Für überladene Methoden wird zur Unterscheidung auch noch die Parameterliste als Class-Objekt benötigt.

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 Funktion getMethod() 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, wird die Zielmethode mittels invoke() ausgeführt – im englischen wird dies dynamic invocation genannt. invoke() erwartet zwei Argumente: ein Array mit Funktions-Argumenten, die der aufgerufenen Methode übergeben werden, und eine Referenz auf das Objekt, auf dem die Methode aufgerufen werden soll und zur Auflösung der dynamischen Bindung dient.


final class java.lang.reflect.  Method
  extends AccessibleObject
implements GenericDeclaration, Member

gp  Object invoke( Object obj, Object... args ) Ruft eine Methode des Objektes obj mit dem gegebenen Argumenten auf. Sind keine Argumente zu übergeben, wird null auf Object[] angepasst. Das folgende Beispiel zeigt, dass die Typanpassung auch für getMethod() nötig ist.

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

Listing 21.15   InvokeMethod.java

import java.awt.*;
import java.lang.reflect.*;
class InvokeMethod
{
  public static void main( String[] args ) throws Exception
  {
    Point p = new Point( 100 );
    Method method = p.getClass().getMethod( "getX" );
    String returnType = method.getReturnType().getName();
    System.out.print( "(" + returnType + ") " );
    Object returnValue =   method.invoke  ( p(Object[]) null );
    System.out.println( returnValue );
  }
}

Und die Ausgabe ist:

(double) 10.0

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

Deutlich auffallender ist die Möglichkeit der variablen Argumente bei Methoden, denen etwas übergeben wird.

Point p = new Point();
Method method = p.getClass().getMethod( "setLocation",   int.classint.class   );
method.invoke( p,   12   );
System.out.println( p );

Bei getMethod() sind die Parametertypen und bei invoke() die Argumente für setLocation() Beispiele der Varags in Java 5.


Galileo Computing

21.4.1 Statische Methoden aufrufen  downtop

Wir wollen ein Beispiel programmieren, in dem die Klasse InvokeMain die main()-Funktion einer anderen Klasse HasMain mit einem Parameter aufruft.

Listing 21.16   InvokeMain.java

import java.lang.reflect.*;
public class InvokeMain
{
  public static void main( String[] args ) throws Exception
  {
    String[] nargs = "-option""Parameter";
    Method methode = Class.forName( "HasMain" ).getMethod(
                        "main"nargs.getClass() );
    methode.invoke( nullnew Object[]{ nargs } );
  }
}
class HasMain
{
  public static void main( String[] args )
  {
    System.out.println( "Habe mitgebracht einen " + args[1] );
  }
}

Galileo Computing

21.4.2 Dynamische Methodenaufrufe bei festen Methoden beschleunigetoptop

Werden über Reflection Methoden aufgerufen, deren Funktionsnamen erst zur Laufzeit bestimmt werden, so ist die Geschwindigkeit nicht optimal – auch wenn es seit dem Java 5 nicht mehr so schlimm ist. (Doch immer noch zeigt eine einfache Zeitmessung einen deutlichen Unterschied.) Diese Aufrufe lassen sich prinzipbedingt auch durch einen JIT-Compiler nicht weiter beschleunigen. 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 wird dann nur noch benötigt, um eine Unterklasse mit gegebenem Namen zu laden, und die normale, dynamische Methodenbindung erledigt den Rest – ganz ohne versteckte Schnüre, doppelte Böden oder Spiegel (Reflection).


Beispiel   Versuchen wir, den folgenden Code nach diesem Schema zu optimieren.

Listing 21.17   DynamReflection.java

import java.lang.reflect.*;
public class DynamReflection
{
  public static void main( String[] args ) throws Exception
  {
    Class<?> clazz = Class.forName( "DynamReflectionMethod" );
    Object o = clazz.newInstance();
    Method mtd = clazz.getMethod( "meth"new Class[]{} );
    mtd.invoke( onew Object[]{} );
  }
}

DynamReflection ist nun die Hauptklasse, die die Klasse DynamReflectionMethod erst lädt, wenn Class.forName() aufgerufen wird. Über das Class-Objekt erzeugen wir dann mit newInstance() ein neues Exemplar. Anschließend sucht getMethod() die Beschreibung der Methode meth() heraus. Mit invoke() rufen wir diese anschließend auf.

Hier genau liegt der Geschwindigkeitsverlust. Wenn es uns gelingen würde, 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 implementierte Klasse (beziehungsweise Unterklasse) wird dann eine Implementierung angeben.

Listing 21.18   DynamAbstract.java

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( "DynamBaseMethod" );
    DynamBase o = (DynamBase) clazz.newInstance();
    o.meth();
  }
}

DynamBase ist eine Schnittstelle, die zur Übersetzungszeit bekannt ist. Die virtuelle Maschine wird nun den Aufruf selbst nach den üblichen Regeln für die dynamische Bindung auflösen und umsetzen. 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.

 << zurück




Copyright © Galileo Press GmbH 2005
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.


[Galileo Computing]

Galileo Press GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de