Donnerstag, 15. März 2012

Typinferenz in Java

Durch das verstärkte Interesse an funktionalen Sprachen wird auch immer häufiger von Typinferenz gesprochen, also vom automatischen Ermitteln eines passenden Datentyps durch den Compiler.

Beispielsweise legt man in Scala eine Liste ganzer Zahlen mit folgendem Ausdruck an:

val zahlen = List[Int](1,2,3)

Oder noch kürzer:

val zahlen = List(1,2,3)

Der Compiler ermittelt also einen möglichst spezifischen Typen für die rechte Seite des Ausdrucks (hier eine Liste von Ints) und verwendet ihn automatisch als Datentyp für die Wertdeklaration auf der linken Seite.

Seit Version 7 beherrscht nun auch Java eine offensichtliche Form der Typinferenz:

List<Integer> liste = new ArrayList<>();

Man kann auf der rechten Seite die Typangabe bei der generischen ArrayList weglassen (die Angabe der leeren spitzen Klammern, des "Diamanten", ist aber notwendig), und der passende Typ (Integer) wird automatisch von der linken Seite übernommen. Die Inferenz von links nach rechts ist auch der wichtigste Unterschied zu der üblichen Typinferenz funktionaler Sprachen – in der Praxis ist deren Typinferenz von rechts nach links praktischer, da sie mehr Schreibarbeit spart.

Was nicht so offensichtlich ist: Bereits seit Java 5 gibt es eine ähnliche Form der Typinferenz. Definiert man eine Methode mit lokalem generischen Parameter:

public static <T> T typecast(Object obj) {
@SuppressWarnings("unchecked")
T gecastet = (T) obj;
return gecastet;
}

kann man sie wie folgt aufrufen:

String s = typecast("Hallo");

Die Methode "typecast" hat in diesem Fall tatsächlich den Rückgabetyp "String", abhängig vom Typ der deklarierten Variablen auf der linken Seite. Ob diese Methode in der Praxis sinnvoll ist, sei mal dahingestellt – bei Übergabe eines Nicht-String-Objekts geht der Typecast in der Methode zur Laufzeit mit einer ClassCastException schief. Aber sie demonstriert sehr gut die seit langem in Java vorhandene (wenn auch sehr eingeschränkte) Typinferenz.