Introduction
When Java 10 was released in March 2018, it contained only a handful of developer-facing features.
The most visible addition was:
Local Variable Type Inference
commonly known as var.
For over twenty years, Java developers declared variables like this:
Customer customer = customerRepository.findById(id);
Map<String, List<Customer>> customerMap =
new HashMap<>();
CompletableFuture<List<Order>> future =
orderService.fetchOrders();
Notice something?
The compiler already knows the type from the right-hand side.
Yet developers must repeat the same information on the left.
This repetition becomes increasingly verbose with:
- Generics
- Streams
- CompletableFuture
- Optional
- Maps of Lists
- Nested generic types
Java 10 introduced var to eliminate this unnecessary repetition.
However, unlike dynamically typed languages, Java still performs compile-time type checking.
Nothing about Java’s type system changed.
Only the syntax became cleaner.
Learning Objectives
By the end of this article, you will be able to:
- Understand why
varwas introduced. - Learn how Java infers types.
- Know where
varcan be used. - Understand where
varcannot be used. - Write cleaner enterprise code.
- Avoid common readability mistakes.
- Apply coding standards for Spring Boot projects.
Before Java 10
Consider a typical Spring Boot service.
Map<String, List<Customer>> customerMap =
new HashMap<String, List<Customer>>();
Customer customer =
customerRepository.findById(id);
CompletableFuture<List<Order>> future =
orderService.fetchOrders();
The compiler already knows every type.
Yet developers must write them twice.
Java 10 Solution
var customerMap =
new HashMap<String, List<Customer>>();
var customer =
customerRepository.findById(id);
var future =
orderService.fetchOrders();
Much shorter.
Still completely type-safe.
How Does var Work?
Consider:
var customer =
customerRepository.findById(id);
Many developers assume customer has no type.
That is incorrect.
The compiler transforms it into:
Customer customer =
customerRepository.findById(id);
var is resolved at compile time.
There is no runtime type inference.
The generated bytecode is the same as if you had written the explicit type.
Java Is Still Statically Typed
This code:
var number = 10;
creates:
int number = 10;
Later:
number = "Java";
Compilation fails.
Unlike JavaScript or Python, the variable’s type never changes.
Type Inference Examples
Strings
var language = "Java";
Type:
String
Integers
var count = 100;
Type:
int
Collections
var countries =
List.of(
"India",
"USA",
"Japan");
Type:
List<String>
Streams
var names =
customers.stream()
.map(Customer::getName)
.toList();
The inferred type is:
List<String>
Without var, the declaration may become unnecessarily verbose.
Optional
var customer =
repository.findById(id);
Type:
Optional<Customer>
The compiler preserves the exact generic type.
CompletableFuture
var future =
CompletableFuture.supplyAsync(
this::loadCustomer);
The inferred type includes the generic parameter, improving readability by removing repetition.
Where Can var Be Used?
var is allowed only for local variables whose type can be inferred.
Examples include:
var customer = repository.findById(id);
var list = new ArrayList<String>();
for (var customer : customers) {
}
try (var reader =
Files.newBufferedReader(path)) {
}
Where Can var NOT Be Used?
Fields
public class CustomerService {
var repository;
}
Not allowed.
The compiler cannot infer a field’s type from context.
Method Parameters
public void save(var customer)
Not allowed in Java 10.
(Local variable syntax for lambda parameters was introduced later under specific conditions.)
Return Types
public var find()
Not allowed.
Method signatures must declare explicit return types.
Without Initialization
var customer;
Compilation error.
The compiler has no information to infer the type.
Assigning null
var customer = null;
Compilation error.
null does not provide enough type information.
Enterprise Examples
Repository
Before:
Optional<Customer> customer =
repository.findById(id);
After:
var customer =
repository.findById(id);
Streams
Before:
List<CustomerDto> dtoList =
customers.stream()
.map(mapper::convert)
.toList();
After:
var dtoList =
customers.stream()
.map(mapper::convert)
.toList();
Configuration
var config =
Map.of(
"CACHE", "REDIS",
"ENV", "PROD");
When var Improves Readability
Good example:
var orders =
orderRepository.findAll();
The variable name and assignment make the type obvious.
Another good example:
var formatter =
DateTimeFormatter.ISO_DATE;
The right-hand side clearly communicates the type.
When var Hurts Readability
Consider:
var result = calculate();
What is result?
- String?
- Customer?
- Order?
- Boolean?
The type is unclear.
In such cases, an explicit type may improve readability.
Another example:
var x = process(a, b);
Without understanding process(), the type is impossible to determine.
var and Generics
Without var:
Map<String,
List<Customer>> customers =
new HashMap<>();
With var:
var customers =
new HashMap<String,
List<Customer>>();
Cleaner.
The compiler still knows the complete generic type.
Performance Myths
Many developers ask:
Does
varimprove performance?
No.
The generated bytecode is effectively identical.
var is purely a source code readability feature.
It has:
- No runtime overhead.
- No performance improvement.
- No JVM optimization impact.
Spring Boot Best Practices
Use var for:
- Stream pipelines.
- Repository results.
- Complex generic types.
- Builder chains.
- Local helper variables.
Avoid var when:
- The inferred type is not obvious.
- The variable has a broad scope.
- Readability suffers.
Consistency across the codebase is more important than using var everywhere.
Common Mistakes
Replacing Every Type with var
This often reduces readability rather than improving it.
Using Meaningless Variable Names
var x = service.process();
Poor.
Prefer descriptive names.
var paymentResponse =
paymentService.process();
Assuming Dynamic Typing
var does not make Java dynamically typed.
The type is fixed at compile time.
Best Practices
✔ Use var when the type is obvious.
✔ Prefer explicit types for public APIs.
✔ Keep variable names meaningful.
✔ Do not overuse var.
✔ Prioritize readability over brevity.
Interview Questions
Is var a keyword?
No.
It is a reserved type name introduced specifically for local variable type inference.
Does var make Java dynamically typed?
No.
Java remains a statically typed language.
Can var be used for fields?
No.
Only local variables with an initializer are supported.
Does var affect performance?
No.
It is resolved at compile time and has no runtime impact.
Should every local variable use var?
No.
Use it where it improves readability. Explicit types are often preferable when the inferred type is unclear.
Summary
Java 10’s var simplifies local variable declarations by allowing the compiler to infer types from context. It reduces boilerplate without changing Java’s type system or runtime behavior.
Used thoughtfully, var improves readability—particularly with complex generics, Streams, Optional, and CompletableFuture. Used indiscriminately, however, it can make code harder to understand.
The key principle is simple:
Use
varto remove redundancy, not to hide information.
Coming Up Next
Part 27 – Java 11 (LTS): The Standard HTTP Client API – Modern HTTP Communication in Enterprise Applications
We’ll explore one of the most significant APIs introduced in Java 11:
- Replacing
HttpURLConnection - Synchronous and asynchronous HTTP requests
- HTTP/2 support
- WebSockets
- Authentication
- Timeouts
- JSON integration
- Calling REST APIs from Spring Boot microservices
- Performance considerations
- Best practices for production systems