Cast Expressions – Functional-Style Programming
Cast Expressions
The context of a cast expression can provide the target type for a lambda expression or a method reference.
In the two statements below, the cast provides the target type of the lambda expression. Note that the textual lambda expressions are identical, but their target types are different.
System.out.println(((IntUnaryOperator) i -> i * 2).applyAsInt(10)); // 20
System.out.println(((DoubleUnaryOperator) i -> i * 2).applyAsDouble(10.0));// 20.0
The first statement below does not compile, as the Object class is not a functional interface and therefore cannot provide a target type. In the second statement, the cast provides the target type of the constructor expression. The type Function<String, StringBuilder> is a subtype of the supertype Object, and the assignment is allowed.
// Object obj1 = StringBuilder::new; // Compile-time error!
Object obj2 = (Function<String, StringBuilder>) StringBuilder::new;
In the code below, the instanceof operator at (1) is used to guarantee that at runtime the cast will succeed at (2) and the lambda expression can be executed at (3). Without the instanceof operator, the cast at (2) will be allowed at compile time, but a resounding ClassCastException will be thrown at runtime, as DoubleUnaryOperator and IntUnaryOperator are not related types. In the code below, the body of the if statement is not executed.
Object uFunc1 = (IntUnaryOperator) i -> i * 2;
if (uFunc1 instanceof DoubleUnaryOperator) { // (1) false
DoubleUnaryOperator uFunc2 = (DoubleUnaryOperator) uFunc1; // (2)
uFunc2.applyAsDouble(10.0); // (3)
}
Nested Lambda Expressions
When lambda expressions are nested, the context of the outer lambda expressions can provide the target type for the inner lambda expressions. This typically occurs when currying functions.
Supplier<Supplier<String>> f = () -> () -> “Hi”;
The target type for the nested lambda expressions is inferred from the context, which is an assignment statement to a reference of type Supplier<Supplier<String>>. The inner lambda expression () -> “Hi” is inferred to be of target type Supplier<String>, as its type () -> String is compatible with the function type of this target type. The outer lambda expression is then inferred to have the type () -> Supplier<String> which is compatible with the target type Supplier<Supplier<String>>.