Generics

+ andere TechDocs
+ Annotations
+ Enums
+


Zu den Neuerungen in Java 5 gehören die im JSR 14 beschriebenen Generics. Mit Generics ("Generizität", "generische Elemente") werden im Java-Umfeld Sprachmittel bezeichnet, mit denen Klassen und Methoden mit Typparametern parametrisiert werden können, um Typsicherheit trotz generischer Programmierung zu ermöglichen.

Generics werden nur während der Kompilierung ausgewertet. Der entstehende Bytecode ist abwärtskompatibel.

Der folgende Text beschreibt nur Grundlagen zu Generics. Weitergehende Informationen finden Sie zum Beispiel unter:



Inhalt

  1. Einfaches Generics-Anwendungsbeispiel
  2. Mischung aus Code mit und ohne Generics
  3. Generische Klasse selbst erstellen
  4. Generische Methode selbst erstellen
  5. Typeinschränkung
    Kovarianz bei Arrays, Invarianz bei Generics, "extends"
  6. Wildcards
    Wildcard-Arten,
    "<? extends>" versus "<T extends>",
    "List" versus "List<?>",
    "List<?>" versus "List<Object>",
    "List<? extends Number>" versus "List<Number>",
    "<? super T>" versus "<? extends T>",
    Beispiel mit "<? super T>" und "<? extends T>"

  7. Generics mit Enums
  8. Wann werden Raw-Typen benötigt?
  9. Diamond Operator (ab Java 7)



Einfaches Generics-Anwendungsbeispiel

Die Syntax von Generics besteht in den einfachen Fällen aus einem Typnamen, der in spitze Winkelklammern eingeschlossen wird und meistens hinter einem zu spezifizierendem Klassennamen steht. Beispielsweise für eine Liste aus Strings:

Ohne Generics:  List meineListe = new ArrayList();
meineListe.add( "Hallo" );
String s = (String) meineListe.get( 0 );
Mit Generics: List<String> meineListe = new ArrayList<String>();
meineListe.add( "Hallo" );
String s = meineListe.get( 0 );

Die Variante mit Generics benötigt beim Lesen keinen Typecast. Noch wichtiger: Wenn statt dem String zum Beispiel ein Integer-Objekt hinzugefügt würde, würde ohne Generics erst zur Laufzeit eine Exception geworfen, während mit Generics der Compiler diesen Fehler bereits während der Kompilierung feststellt:

Ohne Generics:  List meineListe = new ArrayList();
meineListe.add( new Integer(42) );
String s = (String) meineListe.get( 0 ); // <-- Laufzeit-Exception !
Mit Generics: List<String> meineListe = new ArrayList<String>();
meineListe.add( new Integer(42) ); // <-- Kompilierfehler
String s = meineListe.get( 0 );

Das hier gezeigte Hinzufügen von Typinformationen per Generics kann nur auf Interfaces, Klassen und Methoden angewendet werden, die dafür vorbereitet sind, wie zum Beispiel List, ArrayList, weitere Collections und eigene Klassen.



Mischung aus Code mit und ohne Generics

Eine Mischung aus Code mit und ohne Generics sollte nach Möglichkeit vermieden werden, da dies verwirrend sein kann und zu Fehlern führen kann. Aber Java lässt es zu, wie folgendes lauffähiges Beispiel zeigt:

import java.util.*;

public class GenericsTest1
{
   public static void main( String[] args )
   {
      List<String> listeMitGenerics = new ArrayList<String>();
      listeMitGenerics.add( "Hallo1" );
      methodeOhneGenerics( listeMitGenerics );

      List listeOhneGenerics = new ArrayList();
      listeOhneGenerics.add( "Hallo2" );
      methodeMitGenerics( listeOhneGenerics );
   }

   static void methodeOhneGenerics( List lst )
   {
      System.out.println( lst.get( 0 ) );
   }

   static void methodeMitGenerics( List<String> lst )
   {
      System.out.println( lst.get( 0 ) );
   }
}

Der methodeOhneGenerics wird die listeMitGenerics übergeben und umgekehrt wird der methodeMitGenerics die listeOhneGenerics übergeben.

Folgendes Beispiel zeigt, zu welchen Problemen eine Mischung führen kann:

import java.util.*;

public class GenericsTest2
{
   public static void main( String[] args )
   {
      List<String> listeMitGenerics = new ArrayList<String>();
      List listeOhneGenerics = listeMitGenerics;

      System.out.println( listeMitGenerics.getClass() == listeOhneGenerics.getClass() ); // true

      listeOhneGenerics.add( new Integer(42) ); // kein Kompilierfehler (obwohl eigentlich String-Liste)

      String s = listeMitGenerics.get( 0 );     // <-- ClassCastException zur Laufzeit!
   }
}

Während der listeMitGenerics kein Integer-Objekt hinzugefügt werden könnte, ist dies jedoch mit der mit dem "Raw Type" List deklarierten listeOhneGenerics möglich, obwohl die Referenz dieselbe ist. Obwohl es keinen Typecast gibt, wird zur Laufzeit eine ClassCastException geworfen.

Der Grund für dieses Verhalten ist, dass die Generics-Informationen nur zur Kompilierzeit vohanden sind, aber nicht mehr zur Laufzeit, da sie im Bytecode der .class-Datei fehlen (wie der Vergleich der beiden Klassen im Beispiel zeigt).



Generische Klasse selbst erstellen

Folgendes Beispiel zeigt die Definition und Anwendung der einfachen generischen Klasse MeineGenerischeKlasse:

public class GenericsTest3
{
   public static void main( String[] args )
   {
      MeineGenerischeKlasse<Double> mgk = new MeineGenerischeKlasse<Double>();
      mgk.setAttribut( new Double( 3.14 ) );
      Double d = mgk.getAttribut(); // ohne Typecast
      System.out.println( d + " (" + mgk.getAttribut().getClass() + ")" );
   }
}

class MeineGenerischeKlasse<T>
{
   private T attribut;

   void setAttribut( T attribut ) { this.attribut = attribut; }

   T getAttribut() { return attribut; }
}

Die generische Klasse MeineGenerischeKlasse wird mit dem gewünschten Typ über <T> parametrisiert. Unter diesem Namen kann der "parametrisierte Typ" als "Typ-Variable" in den Attributen und Methoden der Klasse verwendet werden. Statt "T" könnte ein beliebiger Name (außer den reservierten) verwendet werden. Üblich sind einzelne große Buchstaben, zum Beispiel T für Typ, E für Element und K/V für Key/Value.

Auch Interfaces können mit Generics definiert werden:

interface MeinStackInterfaceMitGenerics<T>
{
   public void push( T obj );
   public T pop();
}

Statt nur einem einzelnen Typ können auch mehrere Typen übergeben werden:

class MeineGenerischeKlasse3<T1,T2,T3>
{
   void meineMethode( T1 parm1, T2 parm2, T3 parm3 ) { /* ... */ }
}


Generische Methode selbst erstellen

Nicht nur Klassen, auch Methoden können generisch sein:

import java.util.*;

public class GenericsTest4
{
   public static void main( String[] args )
   {
      List<Double> dd = new ArrayList<Double>();
      dd.add( 1.2 ); // Autoboxing
      dd.add( 2.3 );
      dd.add( 3.4 );
      dd.add( 4.5 );
      System.out.println( findeIndex( dd, 3.4 ) );
   }

   static <T> int findeIndex( List<T> meineListe, T meinObjekt )
   {
      for( int i = 0; i < meineListe.size(); i++ ) {
         if( meineListe.get( i ).equals( meinObjekt ) ) return i;
      }
      return -1;
   }
}

Beim Aufruf der generischen Methode findeIndex() wird der Typ nicht explizit übergeben, sondern automatisch erkannt.

Auch hierbei können mehrere Typen vorgegeben werden:

   <T1,T2> T2 meineMethode( T1 parm1, T2 parm2 )
   {
      // ...
   }


Typeinschränkung

Kovarianz bei Arrays

Weil Arrays kovariant sind und Integer von Number abgeleitet ist, ist Integer[] eine Ableitung von Number[]. Dies kann zu Laufzeitfehlern führen:

   Number[] a = new Integer[1];
   a[0] = new Double( 3.14 );  // <-- kein Kompilierfehler, aber Laufzeitfehler

Arrays sind "reified": Sie kennen und überprüfen die Elementtypen zur Laufzeit.

Invarianz bei Generics

Obwohl Integer von Number abgeleitet ist, ist ArrayList<Integer> keine Ableitung von ArrayList<Number> und folgender Ausdruck führt zu einem Kompilierfehler:

   ArrayList<Number> a = new ArrayList<Integer>();  // <-- Kompilierfehler

Dies wird als "Invarianz" bezeichnet: Die Ableitungsbeziehung zwischen Typargumenten überträgt sich nicht auf generische Klassen, es gibt keine Kovarianz bei Generics. Dadurch ist (anders als bei Arrays) Typsicherheit gewährleistet. Generics sind "non-reified", da sie per "Type Erasure" implementiert sind: Die Typen werden zur Kompilierzeit überprüft und anschließend wird die Typinformation entfernt.

Die Zuweisung einer Integer-Liste zu einer Number-Liste ist trotzdem möglich, sie muss nur anders formuliert werden: Entweder per "bounded type parameter" wie im Folgenden gezeigt wird oder per "upper bounded wildcard", wie unten gezeigt wird.

"extends"

Mit dem Schlüsselwort "extends" kann der erlaubte Typ eingeschränkt werden auf Ableitungen einer bestimmten Klasse ("bounded type parameter"):

import java.math.BigDecimal;

public class GenericsTest5
{
   public static void main( String[] args )
   {
      Integer      ii = onlyPositiv( new Integer( 42 ) );
      BigDecimal   bd = onlyPositiv( new BigDecimal( 4711 ) );

      String       s  = "Bla";
      StringBuffer sb = new StringBuffer( "Blubb" );

      System.out.println( laengererText( s, sb ) );
   }

   static <T extends Number> T onlyPositiv( T parm )
   {
      return ( parm.doubleValue() >= 0 ) ? parm : null;
   }

   static <T extends CharSequence> T laengererText( T parm1, T parm2 )
   {
      return ( parm1.length() > parm2.length() ) ? parm1 : parm2;
   }
}

Auch hierbei können wieder mehrere Typen vorgegeben werden:

   <T1 extends Number, T2 extends CharSequence> T2 meineMethode( T1 parm1, T2 parm2 )
   {
      // ...
   }

Über "&" kann die Implementierung zusätzlicher Interfaces gefordert werden, wobei [Type1] eine Klasse sein kann, aber alle weiteren Typen ([IType2], [IType3], ...) Interfaces sein müssen:

   <T extends [Type1] & [IType2] & [IType3]> T meineMethode( T parm )
   {
      // ...
   }

Auch rekursive Verwendung ist möglich ("recursive type bound"):

   <T extends [Type1]<T>> T meineMethode( T parm )
   {
      // ...
   }
   // Vorlaeufiges konkretes Beispiel zur Maximum-Suche in einer Liste mit "mutually comparable" Elementen
   // (weiter unten finden Sie eine verbesserte Version):
   <T extends Comparable<T>> T max( List<T> list )
   {
      T result = list.get( 0 );
      for( T t : list )
         if( t.compareTo( result ) > 0 ) result = t;
      return result;
   }


Wildcards

Wildcard-Arten

Es gibt drei Arten von Wildcards und zusätzlich die Möglichkeit der Verknüpfung mehrerer Typen:

<?>Beliebiger Typ (vermeidet Compiler-Warnung) ("unbounded wildcard")
<? super [Type]>Superklasse von [Type] ("lower bounded wildcard")
<? extends [Type]>Von [Type] (Klasse oder Interface) abgeleiteter Typ ("upper bounded wildcard")
<? extends [Type1] & [IType2] & [IType3]>Subtyp von mehreren Typen (außer dem ersten Typ nur Interfaces erlaubt) ("multiple bounds")

"<? extends [Type]>" versus "<T extends [Type]>"

Zum Beispiel

List<? extends Comparable> meineMethode( List<? extends Comparable> list ) { /* ... */ }

ist in vielen Fällen vergleichbar mit

<T extends Comparable> List<T> meineMethode( List<T> list ) { /* ... */ }

Wenn der Typ <T> weiter zum Beispiel innerhalb der Klasse oder Methode benötigt wird, oder wenn sichergestellt werden muss, dass beim Parameter und bei der Rückgabe identische Typen verwendet werden, muss die zweite Form gewählt werden. Ansonsten kann die erste Variante verwendet werden.

"List" versus "List<?>"

Auch wenn Sie den Typ nicht kennen oder verschiedenartige Objekttypen verwenden wollen, sollten Sie nicht Raw-Typen verwenden, wie zum Beispiel Class oder List, sondern immer generische Typen, wie zum Beispiel Class<?>, List<?> oder List<Object>.

Bei Raw-Typen kann es leicht zu den oben beschriebenen Fehlern und zur ClassCastException kommen. Bei generischen Typen (wie z.B. List<?> und List<Object>) verhindert der Compiler (wenn Sie keine Warnungen unterdrücken) eine ClassCastException und die Verwendung nicht erlaubter Methoden.

Außerdem kann bei Verwendung von Raw-Typen Information verloren gehen. Folgende Zeilen (mit generischem Class<?>) funktionieren fehlerfrei, weil die Klasseninformation als "Runtime Type Token" sowohl Kompilier- als auch Laufzeitinformationen enthält (ersetzen Sie meinpackage.MeineKlasse z.B. durch java.lang.String und MeineAnnotation durch Deprecated):

Class<?> c = Class.forName( "meinpackage.MeineKlasse" );
MeineAnnotation a = c.getAnnotation( MeineAnnotation.class );

Diese Zeilen (ohne <?>) sind dagegen nicht kompilierbar:

Class c = Class.forName( "meinpackage.MeineKlasse" );
MeineAnnotation a = c.getAnnotation( MeineAnnotation.class );

Das folgende Beispiel zeigt, wie Class<?> für einen typsicheren heterogenen Container genutzt werden kann, der Instanzen verschiedener Typen typsicher speichern kann:

class TypsichererHeterogenerContainer
{
   private Map<Class<?>,Object> map = new HashMap<Class<?>,Object>();

   public <T> void putObject( Class<T> clss, T obj ) { map.put( clss, clss.cast( obj ) ); }

   public <T> T getObject( Class<T> clss ) { return clss.cast( map.get( clss ) ); }
}

Interessant ist dabei der "dynamische Typecast" mit "clss.cast()" in getObject(). In putObject() ist ebenfalls ein solcher Typecast: Er dient aber lediglich zur Vermeidung von Fehlern durch Raw-Typen.

"List<?>" versus "List<Object>"

Zwischen den generischen Typen List<?> und List<Object> gibt es wichtige Unterschiede. Während folgende Zuweisung erlaubt ist:

List<?>      wildcardListe = new ArrayList<Integer>();

führt folgende Zuweisung zu einem Kompilierfehler (wegen der Invarianz von Generics):

List<Object> objectListe   = new ArrayList<Integer>();  // <-- Kompilierfehler

Auch bei der Verwendung der Methoden kann es Einschränkungen geben. Während folgende Zeilen erlaubt sind:

List<Object> objectListe   = new ArrayList<Object>();
objectListe.add( new Integer(42) );

führen folgende Zeilen zu einem Kompilierfehler:

List<?>      wildcardListe = new ArrayList<Integer>();
wildcardListe.add( new Integer(42) );  // <-- Kompilierfehler

Einer List<Object> kann also keine List<Integer> zugewiesen werden, aber es können Integer-Objekte über add() hinzugefügt werden.
Bei einer List<?> ist es umgekehrt: Ihr kann eine List<Integer> zugewiesen werden, aber es können keine Integer-Objekte über add() hinzugefügt werden.

Dies mag anfänglich widersprüchlich erscheinen. Der Grund ist folgender:

"List<? extends Number>" versus "List<Number>"

Die Unterschiede zwischen den generischen Typen List<? extends Number> und List<Number> sind in etwa dieselben wie die oben genannten zwischen List<?> und List<Object>. Während folgende Zuweisung einer Integer-Liste erlaubt ist:

List<? extends Number> extNumberListe = new ArrayList<Integer>();

führt folgende Zuweisung zu einem Kompilierfehler (wegen der Invarianz von Generics):

List<Number>           numberListe    = new ArrayList<Integer>();  // <-- Kompilierfehler

Auch bei der Verwendung der Methoden gibt es wieder Einschränkungen. Während folgende Zeilen erlaubt sind:

List<Number>           numberListe    = new ArrayList<Number>();
numberListe.add( new Integer(42) );

führen folgende Zeilen zu einem Kompilierfehler:

List<? extends Number> extNumberListe = new ArrayList<Integer>();
extNumberListe.add( new Integer(42) );  // <-- Kompilierfehler

Einer List<Number> kann also keine List<Integer> zugewiesen werden, aber es können Integer-Objekte über add() hinzugefügt werden.
Bei einer List<? extends Number> ist es umgekehrt: Ihr kann eine List<Integer> zugewiesen werden, aber es können keine Integer-Objekte über add() hinzugefügt werden.
Auch wenn es anfänglich seltsam erscheint, dass einer <? extends Number>-Liste kein Integer hinzugefügt werden kann, so ist der Grund doch einfach: Da der <? extends Number>-Typ unbekannt ist, könnte es ja auch eine Double-Liste sein, und dann könnte ein hinzugefügter Integer zu einer ClassCastException führen.

"<? super T>" versus "<? extends T>"

Wann muss "<? super T>" und wann "<? extends T>" verwendet werden?
Die grundsätzliche Regel für Input-Parameter lautet:
Bei "Konsumenten" wird "<? super T>" und bei "Produzenten" "<? extends T>" verwendet.

"Comparable" ist immer ein Konsument, weil er T-Instanzen verwendet ("verbraucht"). Deshalb sollte immer "Comparable<? super T>" verwendet werden. Analoges würde für "Comparator" gelten. Falls ein Collection-Inputparameter als Input für ein Ergebnis fungiert (also Instanzen konsumiert), kann auch hierbei "Collection<? super E>" sinnvoll sein (beispielsweise in folgender Stack-Pop-Methode: "void popAll( Collection<? super E> destination )"). Im Allgemeinen wird "super" in eigenen Deklarationen eher selten verwendet.

Bei Input-Parametern, die sowohl als Konsument als auch als Produzent verwendet werden, machen Wildcards keinen Sinn.

Auch bei Return-Typen sollten Wildcards möglichst vermieden werden.

Im folgenden Beispiel dient der Methodenparameter "List<? extends T>" als Produzent, da er T-Instanzen zur Verfügung stellt. Deshalb wird "extends" verwendet.

Beispiel mit "<? super T>" und "<? extends T>"

Nicht alle das Interface Comparable implementierende Klassen implementieren Comparable<EigeneKlasse>, einige implementieren Comparable<SuperKlasse>. Die im folgenden Beispiel gezeigte Klasse AtomicLongComparable ist hierfür ein Beispiel: Sie implementiert Comparable<AtomicLong>, was sinnvoll ist, damit außer mit AtomicLongComparable-Objekten auch mit AtomicLong-Objekten verglichen werden kann.

Für solche Klassen funktioniert die oben gezeigte Deklaration einer generischen max()-Methode nicht, sondern es muss eine verbesserte Variante verwendet werden. Das folgende Beispiel zeigt sowohl die ursprüngliche (hier nicht einsetzbare) findeMaximumOhneWildcards()-Methode als auch die neue findeMaximumMitWildcards()-Methode mit verbesserter generischer Deklaration. (Die Programmierung der findeMaximum...()-Methoden erfolgt nur zur Erläuterung der Deklarationsoptionen, in der Praxis sollte natürlich stattdessen Collections.max() verwendet werden.)

Damit das Beispiel kompilier- und lauffähig ist, muss die Zeile mit dem Kompilierfehler auskommentiert werden. Der Komplilierfehler lautet: "Bound mismatch: The generic method [...] is not applicable for the arguments [...]. The inferred type [...] is not a valid substitute for the bounded parameter [...]".

import java.util.*;
import java.util.concurrent.atomic.AtomicLong;

public class GenericsTest6
{
   public static void main( String[] args )
   {
      List<AtomicLongComparable> lngLst = new ArrayList<AtomicLongComparable>();
      lngLst.add( new AtomicLongComparable() );
      lngLst.add( new AtomicLongComparable( 99 ) );
      lngLst.add( new AtomicLongComparable( 42 ) );

      System.out.println( findeMaximumMitWildcards(  lngLst ) ); // funktioniert
      System.out.println( findeMaximumOhneWildcards( lngLst ) ); // <-- Kompilierfehler
   }

   // Verbesserte Version mit Wildcards:
   static <T extends Comparable<? super T>> T findeMaximumMitWildcards( List<? extends T> list )
   {
      T result = list.get( 0 );
      for( T t : list )
         if( t.compareTo( result ) > 0 ) result = t;
      return result;
   }

   // Urspruengliche Version ohne Wildcards:
   static <T extends Comparable<T>> T findeMaximumOhneWildcards( List<T> list )
   {
      T result = list.get( 0 );
      for( T t : list )
         if( t.compareTo( result ) > 0 ) result = t;
      return result;
   }
}

class AtomicLongComparable extends AtomicLong implements Comparable<AtomicLong>
{
   private static final long serialVersionUID = 0L;

   public AtomicLongComparable() { super(); }
   public AtomicLongComparable( long lng ) { super( lng ); }

   @Override public int compareTo( AtomicLong o )
   {
      return ( o == null || get() > o.get() ) ? 1 : ( get() < o.get() ) ? -1 : 0;
   }
}


Generics mit Enums

Die Verwendung von Generics mit Enum-Klassen ist ähnlich wie bei anderen Klassen. In folgendem Beispiel implementiert das Enum MeinEnum das Interface Calculator. Die beiden Methoden testAllCalculators1() und testAllCalculators2() demonstrieren zwei verschiedene Möglichkeiten für Methodendeklarationen, um für alle Enum-Elemente die calculate()-Berechnungsmethode für den Beispielparameter 42 aufzurufen.

Informationen zu Enums finden Sie unter java-enums.htm.

import java.util.*;

public class GenericsTest7
{
   public static void main( String[] args )
   {
      int i = 42;
      testAllCalculators1( MeinEnum.class, i );
      testAllCalculators2( Arrays.asList( MeinEnum.values() ), i );
   }

   // Variante 1:
   static <T extends Enum<T> & Calculator> void testAllCalculators1( Class<T> calcs, int i )
   {
      for( Calculator c : calcs.getEnumConstants() )
         System.out.println( c + ": " + i + " -> " + c.calculate( i ) );
   }

   // Variante 2:
   static void testAllCalculators2( Collection<? extends Calculator> calcs, int i )
   {
      for( Calculator c : calcs )
         System.out.println( c + ": " + i + " -> " + c.calculate( i ) );
   }
}

interface Calculator
{
   int calculate( int i );
}

enum MeinEnum implements Calculator
{
   ITEM1 { public int calculate( int i ) { /* ... */ return 2 * i; } },
   ITEM2 { public int calculate( int i ) { /* ... */ return 7 * i; } };
}


Wann werden Raw-Typen benötigt?

Normalerweise sollten Raw-Typen nicht mehr verwendet werden. Aber es gibt zwei Ausnahmen:

In Klassenbezeichnungen ("Class Literals") können keine Generics verwendet werden, sondern nur Raw-Typen:
"List.class", "String[].class", "int.class" und "void.class" sind erlaubt,
aber "List<String>.class" und "List<?>.class" nicht.

Ähnliches gilt für den instanceof-Operator:
"obj instanceof List" ist erlaubt, aber "obj instanceof List<String>" nicht.
Eine entsprechende Abfrage könnte folgendermaßen aussehen:

if( obj instanceof List ) {
   List<?> lst = (List<?>) obj;
   // ...
}

Beachten Sie, dass es Methoden gibt, bei denen man den Eindruck bekommen kann, dass Generics vergessen wurden. Beispielsweise ist Folgendes nicht kompilierbar ("cannot convert from Object[] to String[]") obwohl es eigentlich korrekt aussieht:

List<String> list = Arrays.asList( new String[] { "abc", "xyz" } );
String[] array = list.toArray();

Abhilfe ist bei diesem Beispiel leicht möglich:

List<String> list = Arrays.asList( new String[] { "abc", "xyz" } );
String[] array = list.toArray( new String[list.size()] );


Diamond Operator (ab Java 7)

Beispielsweise eine Map von String-Listen wird normalerweise folgendermaßen definiert:

Map<String,List<String>> strListenMap = new HashMap<String,List<String>>();

Ab Java 7 erlaubt der Compiler auch eine verkürzte Schreibweise. Sind alle Typinformationen bekannt, so können beim new-Operator die Typparameter entfallen und es genügen die spitzen Klammern (als "Diamond Operator"):

Map<String,List<String>> strListenMap = new HashMap<>();



Weitere Themen: andere TechDocs | Annotations
© 2008-2010 Torsten Horn, Aachen