Part 1 of 6 - Essential Java 17+ features for senior developers upgrading from Java 8, 11, or 13.
The var keyword eliminates redundant type declarations, letting the compiler infer types from context. This article explores type inference rules, best practices, and shows why it's essential for modern Java development with the Stream API.
Why This Matters
Java has been criticized for verbosity forever. When modern languages got type inference decades ago, Java developers had to write types explicitly everywhere. By 2018, with the Stream API introducing complex generic types, the pain became unbearable:
Map<String, List<Transaction>> transactions = new HashMap<String, List<Transaction>>();
That's noise. The compiler already knows the type from the right side—why repeat it?
The var keyword (Java 10) solved this: "Let the compiler figure out the type, and let developers focus on logic."
What is var?
The var keyword lets the compiler automatically infer the type of a local variable from its initialization. It's NOT dynamic typing—the type is determined at compile-time and never changes.
var name = "John"; // String
var count = 42; // int
var numbers = List.of(1, 2, 3); // List<Integer>
var map = new HashMap<String, Integer>(); // HashMap<String, Integer>
// var eliminates repetition without sacrificing type safety
Key rules:
- Only for local variables (not fields, parameters, or return types)
- Must be initialized
- Cannot change type after assignment
Simple Example
public class VarExample {
public static void main(String[] args) {
var greeting = "Hello, World!";
var numbers = List.of(1, 2, 3, 4, 5);
var total = numbers.stream()
.mapToInt(Integer::intValue)
.sum();
System.out.println(greeting);
System.out.println("Numbers: " + numbers);
System.out.println("Total: " + total);
}
}
Output:
Hello, World!
Numbers: [1, 2, 3, 4, 5]
Total: 15
Key Insight
var reduces boilerplate without sacrificing type safety. It's particularly valuable with the Stream API and complex generic types, where explicit types become noise rather than documentation.
When to Use var
✅ Perfect for:
// Long generic types - clarity without repetition
var customers = repository.findActiveCustomers();
// Stream operations - complex chains
var result = employees.stream()
.filter(e -> e.salary() > 50000)
.map(e -> e.name())
.collect(Collectors.toList());
// Clear from context - type is obvious
var now = LocalDateTime.now();
var file = new File("data.txt");
var json = jsonParser.parse(data);
❌ Avoid when:
// Type is NOT obvious to reader
var result = calculateSomething(x, y); // What type is result?
// Public API - type should be explicit
public var getData() { ... } // Not allowed anyway
// Too cryptic abbreviations
var x = 10; // Better: var count = 10;
var cfg = new Configuration(); // Better: var config = ...
Common Pitfalls
Don't initialize with null:
var x = null; // Compiler error: cannot infer type from null
Can't use with multiple statements:
var name, age; // Compile error: must initialize
// Instead use explicit types
String name;
int age;
Type is determined once and fixed:
var number = 42; // int
number = "string"; // Compile error! number is int, not String
Read the Full Article
Discover much more in Part 1: Introduction & var Keyword on blog.9mac.dev:
- Type inference with anonymous classes and intersection types
- Advanced use cases with collections and functional interfaces
- Best practices from production codebases
- Common pitfalls and how to avoid them
- 15+ detailed examples with unit tests
- Performance implications
- Integration with other Java 17+ features
GitHub Repository
All code examples are ready to clone and run:
git clone https://github.com/dawid-swist/blog-9mac-dev-code.git
cd blog-post-examples/java/2025-10-25-java17-features-every-senior-developer-should-know
../../gradlew test
Next in the series: Part 2 - Records (immutable data carriers)
Top comments (0)