Skip to content

Java 10

Neelu edited this page Jul 26, 2026 · 1 revision

☕ Java 10: Local Variable Type Inference

Release Date: March 20, 2018 | Type: Standard (non-LTS) | Status: ⚠️ End of Life


📋 Table of Contents

  1. Overview
  2. Core Features
  3. Best Practices

🎯 Overview

Java 10 is a smaller release, but it introduced the single most visible day-to-day change since lambdas: the var keyword. It also began Java's move to a 6-month release cadence, which is why versions from here on ship faster with smaller, focused feature sets.

flowchart LR
    A["Explicit Types\nMap<String, List<Order>> orders = new HashMap<>();"] --> B["var\nvar orders = new HashMap<String, List<Order>>();"]
    B --> C["Compiler infers\nfull type at compile time"]
Loading

🚀 Core Features

1️⃣ Local Variable Type Inference (var)

What: The compiler infers the type of a local variable from its initializer. var is not a dynamic type (unlike JavaScript's var) — the type is fixed at compile time, just not written out.

// Before Java 10
Map<String, List<Order>> ordersByCustomer = new HashMap<>();
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
StringBuilder sb = new StringBuilder();

// With var
var ordersByCustomer = new HashMap<String, List<Order>>();
var names = Arrays.asList("Alice", "Bob", "Charlie");
var sb = new StringBuilder();

// Still fully statically typed!
var sb2 = new StringBuilder();
sb2.append(42);      // ✅ fine
// sb2 = 42;          // ❌ compile error — sb2 is a StringBuilder, always

Where var can and can't be used:

// ✅ Local variables with initializers
var count = 10;

// ✅ Enhanced for-loop
for (var order : orders) { ... }

// ✅ Traditional for-loop
for (var i = 0; i < 10; i++) { ... }

// ✅ try-with-resources
try (var conn = dataSource.getConnection()) { ... }

// ❌ Fields
// private var name;  // NOT allowed

// ❌ Method parameters (until Java 11 added it for lambdas)
// void process(var input) { }  // NOT allowed

// ❌ No initializer — type can't be inferred
// var x;  // compile error

// ❌ Initializing with null
// var y = null;  // compile error — no type to infer

2️⃣ Optional.orElseThrow()

// Java 8 required a supplier for a custom exception,
// or get() which threw NoSuchElementException with a poor message
Optional<User> user = findUser(id);

// Java 10: no-arg overload throws NoSuchElementException clearly
User u = user.orElseThrow();

3️⃣ Unmodifiable Collectors

List<String> immutableList = names.stream()
    .filter(n -> n.length() > 3)
    .collect(Collectors.toUnmodifiableList());

Set<String> immutableSet = names.stream()
    .collect(Collectors.toUnmodifiableSet());

Map<String, Integer> immutableMap = names.stream()
    .collect(Collectors.toUnmodifiableMap(n -> n, String::length));

4️⃣ Application Class-Data Sharing (AppCDS)

What: Extends class-data sharing (previously JDK classes only) to application classes, reducing JVM startup time and memory footprint by sharing class metadata across JVM instances.

# Generate a class list
java -Xshare:off -XX:DumpLoadedClassList=app.classlist -jar app.jar

# Create the shared archive
java -Xshare:dump -XX:SharedClassListFile=app.classlist \
     -XX:SharedArchiveFile=app-cds.jsa -cp app.jar

# Run using the archive (faster startup)
java -Xshare:on -XX:SharedArchiveFile=app-cds.jsa -cp app.jar Main

Why it matters: Meaningful for microservices/serverless where JVM startup time directly affects cold-start latency.


5️⃣ Time-Based Release Versioning

From Java 10 onward, version numbers follow $FEATURE.$INTERIM.$UPDATE.$PATCH and ship every 6 months (March/September), regardless of feature readiness — unfinished features simply stay in "preview" until ready. This is why you'll see the same feature listed across several consecutive versions as it matures.

timeline
    title Java Release Cadence (post-Java 9)
    2018-03 : Java 10
    2018-09 : Java 11 (LTS)
    2019-03 : Java 12
    2019-09 : Java 13
    2020-03 : Java 14
    2020-09 : Java 15
    2021-03 : Java 16
    2021-09 : Java 17 (LTS)
Loading

💡 Best Practices

✅ Use var when the right-hand side already makes the type obvious (var list = new ArrayList<String>())
✅ Use var heavily in loop variables and try-with-resources — low risk, high readability win
❌ Avoid var when the initializer doesn't make the type clear (var result = process(x) — what's result?)
❌ Don't use var for public API signatures, fields, or return types — those still need explicit types, and var isn't allowed there anyway

// ❌ Reduces readability — reader must dig into process() to know the type
var result = process(data);

// ✅ Type is obvious from the right-hand side
var users = new ArrayList<User>();
var isValid = validate(input);   // boolean is obvious enough from context

Clone this wiki locally