Maciej Soliński
Wróć do bloga

var w Javie — wygoda czy utrata czytelności?

Kiedy var upraszcza kod, kiedy ukrywa ważny typ i jak ustalić użyteczną konwencję dla zespołu.

var w Javie — wygoda czy utrata czytelności?

W kodzie spędzamy dużo czasu na czytaniu, nie tylko na pisaniu. Dlatego skrócenie deklaracji zmiennej o kilkanaście znaków nie jest samo w sobie argumentem za zmianą. Ważniejsze jest to, czy czytelnik nadal rozumie kod bez szukania dodatkowych informacji.

Moje podejście do var jest zachowawcze: używać go tam, gdzie typ jest oczywisty albo nie ma znaczenia dla zrozumienia danej operacji. Gdy typ wnosi ważną informację, wolę go zostawić.

var nie oznacza dynamicznego typowania

var pojawiło się w Javie 10 jako wnioskowanie typu zmiennej lokalnej. Kompilator ustala typ na podstawie inicjalizatora. Nie zmienia się on podczas wykonania programu. Nie użyjemy var jako typu pola klasy, wyniku metody ani zwykłego parametru metody. Osobny przypadek to parametry lambdy, dla których Java udostępnia własne reguły użycia var.

var message = "Order created"; // String
// message = 123;             // błąd kompilacji

var count = 0;                // int
var total = 0L;               // long

Zakres rozwiązania opisują JEP 286 i FAQ Project Amber.

Gdy typ znajduje się obok

var price = new BigDecimal("19.99");
var timeout = Duration.ofSeconds(2);

W pierwszym przykładzie typ widać w konstruktorze. W drugim podpowiada go nazwa klasy i fabryki. Dopisanie BigDecimal lub Duration po lewej może być zgodne ze stylem projektu, ale zwykle nie wnosi nowej informacji.

Krótki zakres zmiennej dodatkowo ułatwia użycie var. Jeśli deklaracja i wykorzystanie mieszczą się w kilku liniach, czytelnik ma kontekst przed oczami.

Gdy trzeba przejść do innego pliku

Rozważmy fragmenty z umownymi metodami serwisu:

var result = orderService.process(request);
var data = result.getData();

Nie wiadomo, czy wynikiem jest zamówienie, status operacji, lista błędów czy obiekt odpowiedzi. Nazwy też niewiele pomagają. Czytelniejsze mogą być jawne deklaracje:

OrderConfirmation confirmation = orderService.process(request);
List<OrderLine> lines = confirmation.getLines();

Te typy są przykładowymi typami domenowymi, a nie klasami bibliotecznymi. Istotne jest to, że ujawniają znaczenie danych. Jeśli wynik byłby użyty tylko do natychmiastowego wywołania jednej oczywistej metody, jawny typ mógłby być mniej potrzebny.

Podpowiedzi IDE pomagają, lecz kod czytamy także w przeglądarce podczas code review. Warto oceniać czytelność również bez informacji wyświetlanych przez edytor.

Krótszy zapis może zmienić typ

List<String> names = new ArrayList<>();
var otherNames = new ArrayList<String>(); // ArrayList<String>
var unknownNames = new ArrayList<>();    // ArrayList<Object>

Pierwsza zmienna jest zadeklarowana jako interfejs. Druga otrzymuje typ konkretnej implementacji. W trzeciej zabrakło informacji pozwalającej wywnioskować String.

To ważne przy refaktoringu: zastąpienie jawnego typu przez var nie zawsze jest wyłącznie zmianą wizualną. Inny typ statyczny może wpłynąć na dostępne metody lub wybór przeciążenia. Przed zmianą trzeba obejrzeć użycia zmiennej.

Jawny typ może też pokazywać zamierzony poziom abstrakcji. Deklaracja List<String> mówi, że kod potrzebuje listy, a nie szczególnych możliwości ArrayList.

Przy długich typach korzyść jest większa

Map<String, List<String>> namesByTeam = Map.of(
        "backend", List.of("Anna", "Piotr"));

for (var entry : namesByTeam.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

Wypisywanie Map.Entry<String, List<String>> w pętli nie musi ułatwiać jej czytania. Typ kolekcji jest widoczny, a operacje na wpisie są proste. To przykład, w którym skrócenie deklaracji usuwa szum bez ukrywania sensu.

Konwencja powinna pomagać recenzentowi

W zespole przyjąłbym trzy zasady:

  • Używać var, gdy inicjalizator jasno pokazuje typ i znaczenie danych.
  • Zachować jawny typ, gdy opisuje domenę, kontrakt albo ważną abstrakcję.
  • Oceniać deklarację razem z zakresem zmiennej i jej użyciami.

Takie podejście jest zgodne z kierunkiem wytycznych Project Amber, które traktują czytelność całego fragmentu jako ważniejszą niż mechaniczne skracanie deklaracji.

Nie ma potrzeby przerabiania całego repozytorium tylko po to, żeby ujednolicić liczbę wystąpień var. Warto poprawiać miejsca, w których informacja o typie pomaga zrozumieć zachowanie. To właśnie czytelność powinna być kryterium decyzji.