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."