javaseiten.de   |   Version 0.6
 

3.6. Generische Methoden

Neben Typen, die als generisch definiert werden können, ist es auch möglich innerhalb von Methoden Typparameter zu verwenden. Eine generische Methode ist eine Methode, die Typparameter verwendet.

 

Deklaration von generischen Methoden

Die folgenden Ableitungsregeln, die für die Deklaration von Methoden gelten, enthalten einen Typparameterabschnitt, der vor der Angabe des Ergebnistyps angeordnet ist:

MethodModifiers_opt TypeParamters_opt ResultType Identifier ( FormalParamterList_opt )
    Throws_opt MethodBody
 
TypeParamters:
        < TypeParamterList >
        
TypeParamterList:
        TypeParamter
        TypeParamterList , TypeParamter        
        
TypeParamter:
        TypeVariable TypeBound_opt

Dieser Typparameterabschnitt wird durch das Nichtterminalsymbol TypParameter repräsentiert, das auch innerhalb der Ableitungsregeln für normale Klassendeklarationen verwendet wird (siehe auch: Definition von generischen Typen, Abschnitt 3.2). Unter Beachtung der genannten Ableitungsregeln könnte die folgende gültige Methodendeklaration angegeben werden:

public <E extends Number & Comparable<E>> E minmax(Vector<E> v, boolean mm) 
{ ... }

Der Parameter v der Methode minmax ist vom generischen Typ Vector<E>. Der Typparameter E ist jedoch gebunden, da für diesen mit extends Number & Comparable<E> zwei Schranken angegeben wurden.

 

Aufruf von generischen Methoden

Wird z.B. die Methode minmax mit dem Argument new Vector<Integer> aufgerufen, also

minmax(new Vector<Integer>, true)

wird durch den Compiler E durch Integer ersetzt und damit ist auch der Rückgabewert der Methode vom Typ Integer. Der konkrete Aufruf ist gültig, da Integer die Klasse Number erweitert und das Interface Comparable<E> implementiert. Die Ableitungsregeln für den Methodenaufruf zeigen, dass Typargumente auch vor dem Namen der Methode (Identifier) platziert werden können. Wildcards (siehe Abschnitt 3.7) können nicht als Typargumente beim Aufruf von generischen Methoden verwendet werden. Um dies zu kennzeichnen, wird das Nichtterminalsymbol NonWildcardTypeArguments verwendet.

MethodInvocation:
        Primary . NonWildcardTypeArguments_opt Identifier ( ArgumentList_opt )
        
Primary:
        ClassInstanceCreationExpression
        
ClassInstanceCreationExpression:   
        new TypeArguments_opt ClassOrInterfaceType ( ArgumentList_opt ) ClassBody_opt

Die letzte Ableitungsregel schafft die Möglichkeit, dass Typargumente explizit einem generischen Konstruktor (siehe Generische Konstruktoren) übergeben werden können. Die Verwendung von Typargumenten bei Methodenaufrufen (siehe 1. Ableitungsregel) verdeutlicht der folgende Ausschnitt aus Listing 3.12:

...
public <E> Vector<E> paraVector() {
  Vector<E> v = new Vector<E>();
  return v;
}  
...
Vector<Double> vd = gme.<Double>paraVector();
...

Durch Verwendung von <Double> beim Methodenaufruf vor dem Methodennamen wird dem Compiler explizit vermittelt, dass der Typparamter E, welcher innerhalb der generischen Methode paraVector verwendet wird, durch Double ersetzt werden soll. Die Methode paraVector gibt ausgehend von einem generischen Typ Vector<E> einen entsprechend parametrisierten Typ zurück. Dabei wäre auch eine Methodenaufruf ohne Verwendung eines Typarguments zulässig:

Vector<Double> vd = gme.paraVector();

Eine generische Methode kann also wie eine reguläre Methode aufgerufen werden. Dies ist möglich, da der Compiler automatisch aus dem Kontext des Methodenaufrufs die Typargumente folgert. So ist z.B. ersichtlich, dass das Objekt vd vom Typ Vector<Double> sein soll und damit wäre E durch Double zu ersetzen. Dieser automatische Vorgang wird auch als Type Argument Inference bezeichnet. Das folgende Beispiel zu generischen Methoden verwendet die Methoden paraVector und minmax. Die Methode minmax ermittelt den Minimal- bzw. Maximalwert aus den Elementen eines Vectors (wird true als zweites Argument übergeben wird der Maximalwert berechnet; bei der Übergabe von false wird der Minimalwert der Elemente ermittelt). Dazu muss das übergebene Typargument das Interface Comparable<E> implementieren, damit mit Hilfe der Methode compareTo ein Vergleich zwischen jeweils zwei Elementen durchgeführt werden kann.

Listing 3.12. GenericMethodExample.java.

/* 
 * GenericMethodExample.java 
 * JDK 5
 *
 */

import java.util.*;

public class GenericMethodExample {
  
  public <E extends Number & Comparable<E>> E minmax(Vector<E> v, boolean mm) {
    Iterator<E> iter = v.iterator();
    E a = iter.next();
    while (iter.hasNext()) {
      E b = iter.next();  
      if ((a.compareTo(b) < 0) && (mm == true)) {
        a = b;  
      }
      if ((a.compareTo(b) > 0) && (mm == false)) {
        a = b;  
      }
    }  
    System.out.println("GenericMethodExample, minmax");
    return a;
  }  
  
  public <E> Vector<E> paraVector() {
    Vector<E> v = new Vector<E>();
    System.out.println("GenericMethodExample, paraVector");
    return v;
  }  
  
  public static void main(String[] args) {
    GenericMethodExample gme = new GenericMethodExample();
        
    Vector<Integer> vi = new Vector<Integer>();
    vi.add(new Integer("12"));
    vi.add(new Integer("4"));
    vi.add(new Integer("11"));
    Integer imin = gme.minmax(vi, false);
    Integer imax = gme.minmax(vi, true);
    System.out.println("min: " + imin.toString() + ", max: " +  imax.toString());
    
    Vector<Double> vd = gme.<Double>paraVector();
    vd.add(new Double(Math.random()));
    vd.add(new Double(Math.random()));
    vd.add(new Double(Math.random()));
    Double dmin = gme.minmax(vd, false);
    Double dmax = gme.minmax(vd, true);
    System.out.println("min: " + dmin.toString() + ", max: " +  dmax.toString());
  }  
}

Die Ausgabe des Listings lautet:

GenericMethodExample, minmax
GenericMethodExample, minmax
min: 4, max: 12
GenericMethodExample, paraVector
GenericMethodExample, minmax
GenericMethodExample, minmax
min: 0.28769658374387863, max: 0.8738327953430263

Die Konsolenausgaben "GenericMethodExample, minmax" und "GenericMethodExample, paraVector" wurden in das Beispiel aufgenommen, da sie für Listing 3.15 von Bedeutung sind.

 

Generische Konstruktoren

Besitzt eine Klasse einen generischen Konstruktor, können bei der Instanzierung dieser Klasse dem Konstruktor Typargumente explizit übergeben werden. Die Angabe dieser Typargumente folgt direkt hinter dem Schlüsselwort new. Das Beispielprogramm GenericConstructorExample.java definiert den generischen Typ GenericConstructorExample<T> und hat einen generischen Konstruktor, der wie folgt deklariert ist:

public <S extends Number> GenericConstructorExample(S s1, S s2, T t) { ... }

Der Konstruktor verwendet die beiden Typparameter S und T. Zur Erzeugung des Objekts gce wird die folgende Anweisung verwendet:

GenericConstructorExample gce = 
        new <Integer> GenericConstructorExample<String>(i1, i2, str);

Die Anweisung zur Objekterzeugung legt S und T fest. Der Typparameter T korrespondiert mit String. S hingegen wird durch die Angabe Integer direkt nach dem Schlüsselwort new festgelegt. Dieses Typargument wird dem Konstruktor explizit übergeben. Diese Übergabe impliziert, dass die Parameter s1 und s2 des Konstruktors ebenfalls vom Typ Integer sein müssen. Dieses wiederum legt auch den korrespondierenden Typ der Argumente i1 und i2 entsprechend fest. Der Konstuktoraufruf, der automatisch bei der Objekterzeugung stattfindet ist gültig, da das Typargument für den Konstruktor die Klasse Number erweitert. Das vollständige kurze Listing zu generischen Konstruktoren lautet wie folgt:

Listing 3.13. GenericConstructorExample.java. Explizite Übergabe eines Typarguments an einen generischen Konstruktor.

/* 
 * GenericConstructorExample.java 
 * JDK 5
 *
 */

import java.util.*;

public class GenericConstructorExample<T> {
  
  private T value;
      
  public <S extends Number> GenericConstructorExample(S s1, S s2, T t) {
    Vector<S> v = new Vector<S>();
    v.add(s1);
    v.add(s2);
    for (int i = 0; i < v.size(); i++) {
      System.out.println(v.get(i).toString());
    }  
    value = t;
  }  
  
  public T get() {
    return value;
  }

  public static void main(String[] args) {
    Integer i1 = new Integer("1");
    Integer i2 = new Integer("2");
    String str = "test";
    GenericConstructorExample gce = 
            new <Integer> GenericConstructorExample<String>(i1, i2, str);
    Object obj = gce.get();
    System.out.println(obj.toString());
  }  
  
}

Die Ausgabe des Beispiels wäre:

1
2
test

 

 

 

Diese Seite nutzt Google-Dienste - siehe dazu Datenschutz.

Copyright © 2006, 2007 Harald Roeder