Type Inference in Generics
1. What Is Type Inference?
Type inference means the Java compiler automatically figures out the type for a generic type parameter.
Instead of telling Java the type yourself, Java figures it out from the context.
static <T> T pick(T a, T b) {
return b;
}
String result = pick("Hello", "World");
Java infers:
T = String
So you don't need to write:
String result = MyClass.<String>pick("Hello", "World");
2. Type Inference in Generic Methods
A generic method can usually be called without specifying its type.
static <T> void print(T value) {
System.out.println(value);
}
Java figures out the type:
print(10); // T = Integer
print("Hello"); // T = String
print(3.14); // T = Double
You can also explicitly specify the type:
MyClass.<Integer>print(10);
But usually this is unnecessary.
3. Diamond Operator <>
The diamond operator lets Java infer the generic type when creating an object.
List<String> names = new ArrayList<>();
Java sees:
List<String>
and infers:
ArrayList<String>
Instead of:
List<String> names = new ArrayList<String>();
Use:
List<String> names = new ArrayList<>();
<>tells Java: "You figure out the type."
4. Type Inference in Generic Constructors
Java can infer types from both the variable type and constructor arguments.
class Box<T> {
<U> Box(U value) {
System.out.println(value);
}
}
Now:
Box<Integer> box = new Box<>("Hello");
Java infers:
T = Integer
U = String
So both generic types can be inferred from the context.
5. Target Type
The target type is the type Java expects the result to be.
Example:
static <T> List<T> emptyList() {
return new ArrayList<>();
}
When you write:
List<String> list = emptyList();
Java sees that the expected type is:
List<String>
Therefore:
T = String
You don't need:
List<String> list =
Collections.<String>emptyList();
6. Target Type with Method Arguments
Target typing also works when passing a generic method's result to another method.
void process(List<String> list) {
System.out.println(list);
}
You can write:
process(Collections.emptyList());
Java sees that process() expects:
List<String>
so it infers:
T = String
7. Type Inference with Lambda Expressions
Java can determine the type of a lambda from the target type.
Predicate<String> p = s -> s.length() > 5;
Java knows:
s → String
because Predicate<String> expects a String.
Another example:
Runnable r = () -> System.out.println("Hello");
Here Java knows the lambda must be a Runnable.
A lambda needs a target type to know what it represents.
8. Type Inference with Overloaded Methods
Suppose there are two methods:
void run(Runnable r) {
r.run();
}
<T> T run(Callable<T> c) {
return c.call();
}
Now:
String result = run(() -> "Done");
The lambda returns "Done", and the expected result is String.
Therefore Java selects:
<T> T run(Callable<T> c)
instead of:
run(Runnable r)
Quick Revision
| Concept | Meaning |
|---|---|
| Type inference | Compiler automatically determines generic types |
| Generic method | Compiler can infer its type parameters |
<> |
Diamond operator; compiler infers type arguments |
| Target type | Expected type that helps Java infer the generic type |
| Lambda | Type is inferred from its target functional interface |
| Constructor | Generic types can be inferred from context |
| Overloading | Target type can help Java choose the correct method |
Easy Example to Remember
List<String> list = new ArrayList<>();
You wrote String only once.
Java automatically understands:
new ArrayList<String>()
That's type inference.
Type inference = "You provide enough information, and the compiler figures out the rest."