What is meant by lambda target type and target type context in Java?
You should understand "target type" as the functional interface as which the function is (intended to be) used.
Think about this: what is this lambda expression expected to be and how can it be used?
() -> "123";
As the book notes it, this expression can't be used on its own. It needs to be associated with a functional interface.
Now, what functional interface can be the type of a lambda expression is picked up from the context. This is where the "target type" of the lambda expression means something.
Consider these examples:
Example 1:
void printString(Supplier<String> supplier) {
System.out.println(supplier.get());
}
You can call that with
printString(() -> "123");
In this case, you mean the type of () -> "123"
to be Supplier<String>
. That's the target type of () -> "123"
in this context.
Example 2:
MyInterface<String> myInt = () -> "123";
As you can see, an identical lambda expression was used, but its target type is now MyInterface<String>
.
Likewise, you can declare another functional interface that has the same signature as MyInterface.func()
and assign the very same lambda expression to it. The target type changes in these varied contexts.
One of the definitions of "target" (taken from here) is:
a result or situation that you intend to achieve.
You can say the result a lambda expression intends to achieve is to implement some functional interface. Hence that functional interface can be seen as the target of that lambda expression, and the type of the functional interface is the target type.
Hence the target type is the type of the functional interface implemented by the lambda expression.
The target type can be inferred based on the context in which the lambda expression is used:
- If the lambda expression is assigned to a functional interface reference variable, the type of that variable is the target type.
- If the lambda expression is returned by some method, the return type of the method is the target type.
- If the lambda expression is passed as an argument to a method, the type of the corresponding argument expected by the method is the target type.
In
(int n) -> n % 2 == 0
the target type is unknown. If you assign this expression to some functional interface reference, that would be the target type.
In
MyInterface<String> myInt = () -> { return "123"; }
the target type is MyInterface<String>
.
I decided to read a bit more about lamdas and found an excellent book "Beginning Java 8 Language Features: Lambda Expressions, Inner Classes, Threads, I/O, Collections and Streams" by Kishori Shiran.
I will just cite a few paragraphs:
Every expression in Java has a type; so does a lambda expression. The type of a lambda expression is a functional interface type. When the abstract method of the functional interface is called, the body of the lambda expression is executed.
Consider the lambda expression that takes a String parameter and returns its length:
(String str) -> str.length()
What is the type of this lambda expression? The answer is that we do not know. By looking at the lambda expression, all you can say is that it takes a String parameter and returns an int, which is the length of the String. Its type can be any functional interface type with an abstract method that takes a String as a parameter and returns an int. The following is an example of such a functional interface:
@FunctionalInterface
interface StringToIntMapper {
int map(String str);
}
The lambda expression represents an instance of the
StringToIntMapper
functional interface when it appears in the assignment statement, like so:
StringToIntMapper mapper = (String str) -> str.length();
In this statement, the compiler finds that the right-hand side of the assignment operator is a lambda expression. To infer its type, it looks at the left-hand side of the assignment operator that expects an instance of the
StringToIntMapper
interface; it verifies that the lambda expression conforms to the declaration of themap()
method in theStringToIntMapper
interface; finally, it infers that the type of the lambda expression is theStringToIntMapper
interface type.This lambda expression may be of different functional interface types depending on the context in which it is used. There are two types of expressions in Java - standalone expressions and poly expressions
A standalone expression is an expression whose type can be determined by the expression without knowing the context of its use. A poly expression is an expression that has different types in different contexts. The compiler determines the type of the expression. The contexts that allow the use of poly expressions are known as poly contexts. All lambda expressions in Java are poly expressions. You must use it in a context to know its type. Poly expressions existed in Java prior to Java 8 and lambda expressions. For example, the expression
new ArrayList<>()
is a poly expression. You cannot tell its type unless you provide the context of its use.The compiler infers the type of a lambda expression. The context in which a lambda expression is used expects a type, which is called the target type. The process of inferring the type of a lambda expression from the context is known as target typing. Consider the following pseudo code for an assignment statement where a variable of type
T
is assigned a lambda expression:
T t = <LambdaExpression>;
The target type of the lambda expression in this context is
T
. The compiler uses the following rules to determine whether the<LambdaExpression>
is assignment compatible with its target typeT
:
T
must be a functional interface type.- The lambda expression has the same number and type of parameters as the abstract method of
T
. For an implicit lambda expression, the compiler will infer the types of parameters from the abstract method ofT
.- The type of the returned value from the body of the lambda expression is assignment compatible to the return type of the abstract method of
T
.- If the body of the lambda expression throws any checked exceptions, those exceptions must be compatible with the declared throws clause of the abstract method of
T
. It is a compile-time error to throw checked exceptions from the body of a lambda expression, if its target type's method does not contain a throws clause.