Typinferenz für lokale Variablen in Java 10, Teil 1

Hinter 'var', formal "Typinferenz für lokale Variablen", versteckt sich in Java 10 ein Feature, das Javas Optik ordentlich umkrempeln wird. Wie funktioniert es und worauf muss man achten?

In Pocket speichern vorlesen Druckansicht 48 Kommentare lesen
Typinferenz für lokale Variablen in Java 10, Teil 1
Lesezeit: 18 Min.
Von
  • Nicolai Parlog
Inhaltsverzeichnis

Am 20. März erschien Java 10. Das zweifellos aufregendste Feature der neuen Version ist var. Damit ihm kann man bei lokalen Variablen den Typ vom Compiler ableiten lassen, statt ihn selbst angeben zu müssen:

var users = new ArrayList<User>();
// equivalent to
ArrayList<User> users = new ArrayList<>();

Das ist alles, was man wissen muss, um mit var loszulegen. Wenn man allerdings etwas genauer hinschaut, ergeben sich allerlei Folgefragen:

  • Wie genau wird der Typ abgleitet?
  • Ist Java jetzt nicht mehr durchgängig statisch typisiert?
  • Wo kann man var überall einsetzen?
  • Wird Code dadurch nicht unleserlicher?
  • Worauf sollte man bei der Verwendung achten?
  • Kann man Variablen mit val, let oder const unzuweisbar machen?

Wenn der Compiler var verarbeitet, erwartet er im gleichen Statement eine Initialisierung mit einem eindeutigen Typ, damit er die Variable mit diesem Typ deklarieren kann. Einen eindeutigen Typ liefern zum Beispiel Konstruktor- oder Methodenaufrufe sowie der ternäre Operator ?:

var usersByName = new HashMap<Name, User>();
var users = loadUsers();
var maybe = 42 > 0 ? "42" : "0";

Das heißt in erster Linie, dass man die Variable initialisieren muss – eine var-Deklaration ohne Zuweisung kompiliert nicht. Die Initialisierung allein reicht allerdings nicht, denn es gibt einige Ausdrücke, die keinen eindeutigen Typ haben, da der Compiler diesen normalerweise von der linken Seite der Deklaration ableitet. Zu diesen sogenannten Poly-Expressions gehören Array-Initialisierer, Lambda-Ausdrücke und Methodenreferenzen. Mit var auf der linken und einer Poly-Expression auf der rechten Seite sind aber zu wenig Typinformationen vorhanden, um etwas abzuleiten. Das bedeutet, dass keine der folgenden Verwendungen von var kompiliert:

// missing initializer ~> compile error
var foo;
foo = "Foo";
// poly expression as initializer ~> compile error
var ints = {0, 1, 2};
var appendSpace = a -> a + " ";
var compareString = String::compareTo

Mit var deklarierte Variablen sind nicht final, und man kann ihnen dementsprechend neue Werte zuweisen. Sie werden beim Ableiten des Typs allerdings nicht berücksichtigt:

// compiler infers type 'String' for 'zoo'
var zoo = "ZOO";
// accordingly, this assignment fails
zoo = 700;

Obwohl es immer einen Typ gibt, der alle Zuweisungen ermöglicht – im Zweifelsfall Object – wählt der Compiler diesen nur, wenn die erste Zuweisung ihn erfordert. Im Beispiel ist zoo eindeutig ein String, weshalb der Variable später kein int zugewiesen werden kann.

Abgesehen von einigen raffinierten Anwendungen ist es also einfach abzuleiten, welchen Typ eine mit var deklarierte Variable hat: den des Ausdrucks auf der rechten Seite.

Auf die Frage, ob mit var geschriebener Code noch statisch typisiert ist, gibt es eine glasklare Antwort: ja. Das Feature hat keine Laufzeitkompenente. Es benötigt keine, denn der Compiler schreibt den abgeleiteten Typ in die resultierenden .class-Dateien. Dem Bytecode ist also gar nicht anzusehen, ob der Typ explizit im Quellcode stand oder abgeleitet wurde.