W3docs

Java var-Schlüsselwort (Typinferenz für lokale Variablen)

var für lokale Variablen in Java: wann es die Lesbarkeit verbessert und wann nicht.

Seit Java 10 können Sie eine lokale Variable mit var deklarieren und den Compiler den Typ aus dem Initialisierer ableiten lassen. var greeting = "hello"; ist bytecode-technisch exakt dasselbe wie String greeting = "hello"; — der Typ ist weiterhin String, Sie haben ihn nur nicht zweimal geschrieben. Das ist Typinferenz für lokale Variablen: eine syntaktische Vereinfachung, die überflüssige Typnamen entfernt, ohne Java dynamisch typisiert zu machen. Richtig eingesetzt reduziert sie Lärm; leichtfertig angewendet verbirgt sie genau die Information, die ein Leser benötigt.

Diese Seite erklärt, was var tut und nicht tut, wo es genau erlaubt ist, wann es sich lohnt, wann es die Lesbarkeit beeinträchtigt, und enthält ein ausführbares Programm, das beweist, dass die abgeleiteten Typen das sind, was Sie erwarten.

var ist Inferenz, kein dynamisches Typisieren

Die wichtigste Tatsache zuerst: var ist kein neuer „Anything"-Typ. Der Compiler liest die rechte Seite, ermittelt den statischen Typ und baut ihn fest ein. Ab diesem Punkt ist die Variable genauso stark typisiert, als hätten Sie den Typ von Hand geschrieben — Sie können ihr keinen unverwandten Typ zuweisen, und der abgeleitete Typ ist zur Kompilierzeit festgelegt.

var name = "Ada";   // name has static type String, forever
name = "Lovelace";  // fine, still a String
name = 42;          // compile error: int cannot be assigned to String

var ist ein reservierter Typname, kein Schlüsselwort — Sie können var weiterhin als Variablen- oder Methodenname verwenden (was Sie aber nicht sollten). Es löst die Inferenz nur in der Position einer lokalen Variablendeklaration aus.

Wo var erlaubt ist — und wo nicht

var funktioniert nur für lokale Variablen mit einem Initialisierer. Der Compiler benötigt eine rechte Seite, um den Typ zu lesen; ohne sie gibt es nichts abzuleiten.

Positionvar erlaubt?Grund
Lokale Variable mit InitialisiererJaDer Initialisierer liefert den Typ
Index/Element in for-SchleifenJaDer Schleifenausdruck liefert den Typ
Try-with-resources-VariableJaDer Ressourcenausdruck liefert den Typ
Lokale Variable ohne InitialisiererNeinNichts zum Ableiten vorhanden
Felder / InstanzvariablenNeinInferenz ist designbedingt nur lokal
MethodenparameterNeinAufrufer, nicht Initialisierer, liefern Werte
MethodenrückgabetypenNeinGleicher Grund wie bei Parametern
Nur mit null initialisiertNeinnull hat keinen konkreten Typ
Lambda-Parameter (nackt)Sonderfall(var x, var y) -> ... ist seit Java 11 erlaubt
var x;                       // error: cannot infer type, no initializer
var nothing = null;          // error: null has no type to infer
public var field = 1;        // error: var not allowed on fields
void m(var p) { }            // error: var not allowed on parameters

Der echte Vorteil: laute Generics zusammenfassen

var zahlt sich aus, wenn der Typname lang, wiederholt oder generics-lastig ist. Der klassische Fall ist eine Deklaration, bei der der Typ auf beiden Seiten des = vollständig erscheint:

// Before: the type name is written twice
Map<String, List<Customer>> byCity = new HashMap<String, List<Customer>>();

// After: the right side already says everything
var byCity = new HashMap<String, List<Customer>>();

Es glänzt auch bei Iteratoren, dem Map.Entry, das Sie aus einer HashMap erhalten, und anderen ausführlichen Typen, die ausgeschrieben keine Klarheit hinzufügen:

for (var entry : byCity.entrySet()) {     // Map.Entry<String, List<Customer>>
  System.out.println(entry.getKey() + " -> " + entry.getValue().size());
}

Wann man var NICHT verwenden sollte

var hilft, wenn der Typ aus der rechten Seite offensichtlich ist, und schadet, wenn das nicht der Fall ist. Wenn ein Leser den Code im Kopf ausführen muss, um den Typ zu kennen, schreiben Sie den Typ aus.

var result = service.process(input);   // unclear: what does process return?
Order result = service.process(input); // clear: an Order

var flag = true;                       // fine, obviously boolean
var count = list.size();               // fine, obviously int

Beachten Sie die Falle bei numerischen Literalen: var leitet den Typ des Literals ab, nicht den Typ, den Sie möglicherweise gemeint haben.

var n = 100;        // int, not long  — for a long you must write 100L or long n
var f = 3.14;       // double, not float
byte b = 1;         // explicit type narrows; var b = 1 would be int

Vermeiden Sie var, wenn dadurch ein bewusster Interface-Typ verloren geht. var list = new ArrayList<String>(); typisiert list als ArrayList<String>, nicht als List<String> — lokal in Ordnung, aber wenn Sie gegen das Interface programmieren möchten, sagen Sie das auch.

Ein ausgearbeitetes Beispiel zum Ausführen

Dieses Programm setzt var an allen erlaubten Positionen ein — einfache Werte, eine generische Map, eine erweiterte for-Schleife, eine indizierte for-Schleife — und verwendet getClass().getSimpleName(), um zu beweisen, dass die abgeleiteten Laufzeittypen genau das sind, was die rechten Seiten impliziert haben.

java— editable, runs on the server

Was man aus dem Programmlauf mitnehmen sollte:

  • greeting.getClass().getSimpleName() gibt String aus und beweist, dass var greeting = "hello" einen echten String erzeugt hat — var ist Kompilierzeit-Inferenz, und zur Laufzeit ist das Objekt genau das, was das Literal impliziert hat, nichts Dynamisches dabei.
  • count + 1 = 43 und price * 2 = 19.98 bestätigen die Inferenzregeln für Zahlen: 42 machte count zu einem int, 9.99 machte price zu einem double. Der Typ des Literals — nicht Ihre Absicht — entscheidet, was die Falle ist, an die man denken muss, wenn man ein long oder float braucht.
  • scores type = HashMap zeigt, dass var den konkreten Typ HashMap der rechten Seite erfasst hat, nicht das Map-Interface; das Diamond <String, List<Integer>> auf der rechten Seite hat dem Compiler alles gegeben, was er brauchte, obwohl die linke Seite nur var enthielt.
  • total chars = 10 stammt aus for (var name : names), wo name als String abgeleitet wurde, sodass name.length() korrekt aufgelöst wurde (3 + 3 + 4) — var funktioniert in erweiterten for-Schleifen und leitet den Elementtyp aus dem Iterable ab.
  • 0..4 sum = 10 stammt aus for (var i = 0; ...), wo i aus dem Literal 0 als int abgeleitet wurde; die indizierte Schleife ist einer der klarsten Orte für var, weil der Typ unverkennbar ist.

Übung

Übung
Bei welcher dieser Deklarationen ist 'var' legal und leitet den in der Anmerkung genannten Typ ab?
Bei welcher dieser Deklarationen ist 'var' legal und leitet den in der Anmerkung genannten Typ ab?

Verwandte Themen

Was this page helpful?