Liigu peamise sisu juurde

var - tüübi tuletamine

Sissejuhatus

Java on staatiliselt tüübitud keel, mis tähendab, et igal muutujal peab olema kindel tüüp, mis määratakse kompileerimise ajal. Traditsiooniliselt see tähendas, et iga deklaratsiooni juurde tuli tüüp selgesõnaliselt välja kirjutada:

List<Map<String, List<Integer>>> data = new ArrayList<>();

Alates Java 10-st saab lokaalsete muutujate puhul kasutada tüübi välja kirjutamise asemel var-i. var ehk lokaalse muutuja tüübituletus (local variable type inference) on keelekonstruktsioon, mis võimaldab kompilaatoril muutuja tüüpi automaatselt tuletada.

var data = new ArrayList<Map<String, List<Integer>>>();

Kompilaator tuletab muutuja tüübi deklaratsiooni parempoolse avaldise põhjal. Muutuja on endiselt staatiliselt tüübitud — selle tüüp fikseeritakse kompileerimisel, kuid seda ei pea enam käsitsi välja kirjutama.

var-i korrektne kasutus

var kehtib ainult meetodi või koodiploki sees deklareeritud muutujate puhul, millel on algväärtus.

Näiteks:

void example() {
var count = 0; // int
var name = "Alice"; // String
var numbers = new ArrayList<Integer>(); // ArrayList<Integer>
var entry = Map.entry("key", 42); // Map.Entry<String, Integer>
}

Samuti on var-i võimalik kasutada ka tsüklites:

for (var item : list) {
System.out.println(item);
}

for (var i = 0; i < 10; i++) {
System.out.println(i);
}

var-i väärkasutus

var-i on võimalik kasutada ainult lokaalsete muutujate puhul, millel on algväärtus. Java kompilaator keelab seda kasutada mitmes teises olukorras, näiteks:

public class Example {
var count = 0; // NOT ALLOWED — field declaration

public var getName() { ... } // NOT ALLOWED — return type

public void process(var input) { ... } // NOT ALLOWED — method parameter
}

Lisaks ei ole lubatud ka lokaalsed deklaratsioonid, millel puuduvad algväärtused või millel on ebaselge tüüp:

var x;              // NOT ALLOWED — no initializer to infer from
var y = null; // NOT ALLOWED — null has no type
var z = {1, 2, 3}; // NOT ALLOWED — array initializer without explicit type

var ei muuda muutuja tüüpi

var on kompileerimisaegne lühend ehk see ei muuda Javat dünaamiliselt tüübitud keeleks. Kui tüüp on tuletatud, käitub muutuja täpselt samamoodi nagu selgesõnaliselt deklareeritud tüübiga muutuja:

var x = 5;      // inferred as int
x = 10; // OK — same type
x = "hello"; // compile error — type is fixed as int

See erineb keeltest nagu Python või JavaScript, kus muutuja võib elu jooksul hoida eri tüüpi väärtusi. Java puhul eemaldab var vaid vajaduse tüüpi välja kirjutada. Kompilaator on jätkuvalt tüübist teadlik ning kontrollib seda.

var koos geneeriliste tüüpidega

var on eriti kasulik siis, kui tüübi nimetus on pikk või sisaldab pesastatud geneerilisi parameetreid. Paremal pool tuleb siiski tüüp täielikult välja kirjutada. var eemaldab korduse ainult vasakult poolelt:

// Without var
Map<String, List<Integer>> scores = new HashMap<>();

// With var
var scores = new HashMap<String, List<Integer>>();
hoiatus

var-i kasutamisel peab paremal poolel tüübiparameeter olema selgesõnaliselt määratud. Kui kasutada ainult teemantoperaatorit <>, tuletab kompilaator tüübiks Object, mis on enamasti liiga üldine:

var list = new ArrayList<>();   // inferred as ArrayList<Object> — probably not what you want
var list = new ArrayList<String>(); // inferred as ArrayList<String> — correct

Millal peaks var-i kasutama

var parandab loetavust siis, kui tüüp on kontekstist selgelt arusaadav ja muutuja nimi annab piisavalt infot:

// Clear — the type is evident from the right side
var users = new ArrayList<User>();
var response = httpClient.send(request);
var path = Path.of("data/config.json");

var halvendab loetavust siis, kui tüüp ei ole kontekstist selge:

// Unclear — what type does this return?
var result = processData(input);

Sellisel juhul annab selgesõnaline tüüp rohkem infot:

List<Report> result = processData(input);

Praktiline rusikareegel: kasuta var-i siis, kui muutuja nimi ja parempoolne avaldis koos teevad tüübi kohe arusaadavaks.

var ei ole märksõna

Tehniliselt ei ole var märksõna, vaid reserveeritud tüübimärk (reserved type name). See tähendab, et pärandkoodi puhul, kus var oli identifikaator, ei teki kompileerimisvigu.

Näiteks:

int var = 5;        // compiles — var is used as a variable name
var var = 5; // also compiles — type inferred as int, variable named var

Praktikas tuleks var-i käsitleda kui märksõna ning seda mitte kasutada muutujate nimena.