Modern Java Evolution
var & Local Type Inference
var (Java 10) infers a local variable's type from its initializer — less ceremony, same static typing. It shines when the type is obvious or noisy, and harms when the reader is left guessing.- Locals with initializers only — no fields, parameters, or return types
- The inferred type is fixed at compile time;
varis notdynamic var map = new HashMap<String, List<Order>>()removes pure noise- With diamond both sides can't infer:
var list = new ArrayList<>()isArrayList<Object> - Style rule: use
varwhen the right-hand side names the type or the variable name carries it
// Good — the type is right there or irrelevant:
var users = new HashMap<String, List<User>>();
var out = new ByteArrayOutputStream();
for (var entry : usersByRole.entrySet()) { ... }
// Bad — what is this?
var result = service.process(data); // reader must open process()
var x = getHandle(); // name AND type opaque
// Trap — inferred type isn't what you meant:
var list = new ArrayList<>(); // ArrayList<Object>!
var price = 10; // int — you wanted BigDecimal?var also captures non-denotable types: an anonymous class's members become accessible (var point = new Object() { int x = 5; }; point.x), and intersection types from generic methods survive. These are curiosities; the everyday win is stripping Map.Entry<String, List<Order>> down to var entry in loop headers.
The style guidance (OpenJDK's own LVTI style guide, echoed by Core Java): var moves information from the declaration to the initializer — fine when the initializer is informative (new X(), factory named like the type, literal), costly when it's a method call with a vague name. Interface-typed declarations (List<String> l = new ArrayList<>() — EJ 64) are one genuine loss; use judgment where the abstraction matters.