Lambda expressions, introduced in Java 8, simplify the process of writing anonymous classes and implementing functional interfaces. Two core concepts that enable their functionality are target typing and type inference. These allow the compiler to deduce the types involved in lambda expressions, resulting in cleaner and more readable code.
What is Target Typing?
Target typing refers to the context in which an expression appears — the compiler uses this context to determine the expected type of the lambda expression.
The target type is the type the Java compiler expects in a specific context — often a functional interface (an interface with a single abstract method).
interface Calculator {
int operation(int a, int b);
}
public class Main {
public static void main(String[] args) {
Calculator add = (a, b) -> a + b; // Target type is Calculator
System.out.println(add.operation(10, 5));
}
}
Code language: PHP (php)
-
(a, b) -> a + b
is a lambda expression. -
The target type is the
Calculator
interface. -
The compiler infers the types of
a
andb
asint
from theoperation(int a, int b)
method.
What is Type Inference?
Type inference is the compiler’s ability to determine types of variables or parameters without explicit type declaration, using information from the context.
Example with Explicit and Inferred Types:
// Explicit
BiFunction<Integer, Integer, Integer> multiply = (Integer a, Integer b) -> a * b;
// Inferred
BiFunction<Integer, Integer, Integer> multiplyInferred = (a, b) -> a * b;
Code language: HTML, XML (xml)
In the second example, the compiler uses the generic types of BiFunction<Integer, Integer, Integer>
to infer that a
and b
are integers.
Lambda Expressions Rely on Target Typing and Type Inference
Example 1: Using Comparator
List<String> names = Arrays.asList("Lotus", "Java", "Prince", "Mahesh");
// Target type = Comparator<String>
Collections.sort(names, (s1, s2) -> s1.compareTo(s2));
Code language: JavaScript (javascript)
The lambda (s1, s2) -> s1.compareTo(s2)
is assigned to a method expecting a Comparator<String>
, so s1
and s2
are inferred as String
.
Example 2: Custom Functional Interface
@FunctionalInterface
interface StringProcessor {
String process(String s);
}
public class Main {
public static void main(String[] args) {
printProcessed("Java", s -> s.toUpperCase()); // Target type is StringProcessor
}
public static void printProcessed(String input, StringProcessor processor) {
System.out.println(processor.process(input));
}
}
Code language: JavaScript (javascript)
Here:
-
s -> s.toUpperCase()
is a lambda. -
Its target type is
StringProcessor
. -
The compiler infers
s
asString
.
Target typing and type inference make lambda expressions in Java powerful and concise. They allow you to write clean, functional-style code without repeating type information. However, it’s important to ensure that lambda expressions are used in contexts where the compiler can infer the expected type — typically when working with functional interfaces.