Wildcards

 "Wildcards" sind ein Begriff aus dem Englischen und beziehen sich auf die Joker im Pokerspiel, die vielfältig eingesetzt werden können. Der Begriff wird im Computerumfeld verwendet wenn es um Platzhalter für andere Zeichen geht.

Unbound Wildcards

Die Wildcards werden benötigt um mit Referenzen auf generische Objekte zu zeigen und deren Ausprägung mehr oder weniger allgemein zu definieren. Eine Referenz auf ein Objekt der verwendeten generischen Klasse Koordinate<T> kann man mit einer Wildcard so beschreiben:

Koordinate<?> zeiger;

Ein Fragezeichen ? ist eine "unbound Wildcard". Sie erlaubt es auf jeden beliebigen Typ der Klasse Koordinate<T> zu zeigen.

Beispiel:

package s2.generics;
/**
 *
 * @author s@scalingbits.com
 */

public class Koordinate<T> {
   private T x;
   private T y;

   public T getX() {return x;}
   public void setX(T x) {this.x = x;}
   public T getY() {return y;}
   public void setY(T y) { this.y = y;}

   public Koordinate(T xp, T yp ) {
      x = xp;
      y = yp;
   }

   @Override
   public String toString() {return "x: " + x + "; y: " + y;}

   public static void main (String[] args) {
      Koordinate<Double> k1 = new Koordinate<Double>(2.2d, 3.3d);
      System.out.println(k1);

      Koordinate<Integer> k2 = new Koordinate<Integer>(2, 3);
      System.out.println(k2);

      Koordinate<Number> k3 = new Koordinate<Number>(2l, 3l);
      System.out.println(k3);

      k3 = new Koordinate<Number>(4.4f, 5.5f);
      System.out.println(k3);

      Koordinate<?> zeiger;
      zeiger = k1;
      zeiger = k2;
      zeiger = k3;
   }
} // Ende der Klasse Koordinate

Die Referenzvariable zeiger kann im gezeigten Beispiel auf beliebig parametrisierte Objekte der Klasse Koordinate<T> zeigen.

Wichtig

Die Wildcard kann nicht in der Sektion des Typ-Parameter einer Klasse, Methode oder Schnittstelle stehen!

Sie kann nur im Kontext von Referenzvariablen verwendet werden!

Umgangssprachlich: Die Wildcard findet man immer nur links des Zuweisungsoperators oder in einer Variablendeklaration.

Die Upper Bound Wildcard

Die bisher verwendeten Typparameter erlauben die Instanziierung einer Klasse mit jeder beliebigen Klasse (die aus der Klasse Object abgeleitet wird). Dies ist oft zu allgemein und kann kontraproduktiv sein.

Bei der Klasse Koordinate macht es keinen Sinn sie mit dem Typ-Parameter Boolean zu parametrisieren:

Koordinate<Boolean> k = new Koordinate<Boolean>(true, true);

Das oben gezeigte Beispiel ist eine korrekte Zuweisung die man übersetzen und ausführen kann. Man kann eine solche Verwendung unterbinden wenn die Typenstruktur Klassenhierachie von Java für Zahlen nutzt:

 

Man kann die Klasse Number als Oberklasse für alle erlaubten Parametrisierungen wählen und den generischen Typ der Klasse Koordinate<T> einschränken:

public class Koordinate<T extends Number> 

Durch diese Notation wird der formale Typparameter auf die Klasse Number oder Spezialisierungen daraus beschränkt. Man spricht von einer "Upper Bound Wildcard" weil die Verwendung von Klassen nach oben (zur Wurzel der Klasse) hin beschränkt ist.

Die Lower Bound Wildcard

Neben der "Upper Bound Wildcard" gibt es auch eine "Lower Bound Wild Card". Sieht wird trotz des ähnlichen Namens, sehr unterschiedlich verwendet.

Sie wird ausschließlich mit der "Unbound Wildcard" in der Schreibweise "? super Lowerbound" bei der Typfestlegung von Referenzen verwendet.

Die generische Klasse selbst ist hier beliebig. Sie sei:

public class MyGenericClass<T> { 
   // Inhalt der Klasse ist nicht wichtig
}

Weiterhin sei eine Klassenhierarchie mit Klassen von A bis F gegeben:

 

mit Hilfe der "Lower Bound Wildcard" kann die Verwendung einer Referenz so eingeschränkt werden, dass nur Instanzen der Klasse C oder Klassen von denen C abgeleitet wurde, verwendet werden darf.

public class TestClass {
...
   public void testCreate(MyGenericClass<? super C> zeiger) {
      ....
   }
   public void test () {
      TestClass.testCreate(new MyGenericClass<C>());      // Korrekt
      TestClass.testCreate(new MyGenericClass<A>());      // Korrekt
      TestClass.testCreate(new MyGenericClass<Object>()); // Korrekt
      TestClass.testCreate(new MyGenericClass<F>());      // Fehler!!
      TestClass.testCreate(new MyGenericClass<D>());      // Fehler!!
   }
}

Die Typ-Parameter der Klassen E, F und B können nicht übergeben werden, da sie nicht in der Typhierarchie der Klasse C vorkommen.

Im Java API werden auch "Lower Bound Wildcards" verwendet. Ein Beispiel ist die drainTo() Methode der Schnittstelle BlockingQueue:

int drainTo(Collection<? super E> c, int maxElements)