Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is meant by lambda target type and target type context in Java?

Tags:

java

lambda

I'm reading a chapter on lambdas in Herbert Schildt's "Java: The Complete Reference" and there are quite a few references to "lambda target type" and "target type context":

A functional interface defines the target type of a lambda expression. Here is a key point: a lambda expression can be used only in a context in which its target type is specified.

Or:

As mentioned earlier, a lambda expression is not executed on its own. Rather, it forms the implementation of the abstract method defined by the functional interface that specifies its target type. As a result, a lambda expression can be specified only in a context in which a target type is defined. One of these contexts is created when a lambda expression is assigned to a functional interface reference. Other target type contexts include variable initialization, return statements, and method arguments, to name a few.

Yet another:

The functional interface associated with a lambda expression can be generic. In this case, the target type of the lambda expression is determined, in part, by the type argument or arguments specified when a functional interface reference is declared.

Can someone please help me understand what's meant by lambda target type?

For instance, in (int n) -> n % 2 == 0 is int the lambda's target type?

Or in:

interface MyInterface<T> {
    T func();
}

MyInterface<String> myInt = () -> { return "123"; }

What is the lambda's target type? Is it String or MyInterface<String>? And what is the lambda's context here?

I read several posts on SO on the topic but still cannot fully understand the notions.

Thanks.

like image 499
John Allison Avatar asked Mar 17 '19 09:03

John Allison


People also ask

What is the target type of a lambda expression in Java?

Lambda Expressions do not have an explicit type. Their type is inferred by looking at the target type of the context or situation. The Target-Type of an expression is the data type that the Java Compiler expects depending on where the expression appears.

What is the target-type of an expression?

The Target-Type of an expression is the data type that the Java Compiler expects depending on where the expression appears. Java 8 supports inference using Target-Type in a method context.

How does the compiler infer the type of a lambda expression?

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.

What does “the target type of this expression must be functional interface” mean?

By now you may have guessed that resolving this error " the target type of this expression must be a functional interface " means functional interface should have only one abstract method. So we need to delete the second method.


2 Answers

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 the map() method in the StringToIntMapper interface; finally, it infers that the type of the lambda expression is the StringToIntMapper 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 type T:

  • 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 of T.
  • 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.
like image 107
John Allison Avatar answered Nov 15 '22 18:11

John Allison


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:

  1. If the lambda expression is assigned to a functional interface reference variable, the type of that variable is the target type.
  2. If the lambda expression is returned by some method, the return type of the method is the target type.
  3. 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>.

like image 26
Eran Avatar answered Nov 15 '22 18:11

Eran