Functional Interfaces#
Functional Interfaces provide target types for lambda expressions and method references.
Each functional interface has a single abstract method (SAM), called the functional method for that functional interface.
Lambda expression’s parameter and return type are matched or adapted to functional method.
Functional interfaces can provide a target type in multiple contexts
Assignment context:
Predicate<String> p = String::isEmpty;
Method invocation context:
stream.filter(e -> e.getSize() > 10)...
Cast context:
stream.map((ToIntFunction) e-> e.getSize())...
Basic function shapes#
Function
(unary function fromT
toR
)R apply(T t)
Consumer
(unary function fromT
tovoid
)accept(T t)
Predicate
(unary function fromT
toboolean
)boolean test(T t)
Supplier
(nullary function toR
)T get()
Consumer<T>
#
Supplier<T>
#
Predicate<T>
#
Function<T, R>
#
Lambda Expression#
Lambdas provide a clear and concise way to represent one method interface using an expression. They are used primarily to define inline implementation of a Functional Interface.
Closure#
A lambda expression can capture variables from the context where it is defined and user the variable within the body. This technique is known as closure and can be used only if a captured variable is final
or effectively final, i.e., a variable or parameter whose value is never changed after it is initialized and if a reference is not changed it is effectively final even if the object reference is changed.
NOTE: This is also applicable to anonymous classes.
final int a = 10; // final
int b = 20;
b++; // As a result, b is not effectively final.
int c = a + b; // effectively final as it is never changed after initialization.
Function<Integer, Integer> fun = n -> n + a + b + c;
https://stackoverflow.com/questions/20938095/difference-between-final-and-effectively-final
Method Reference#
A method reference is a function that refers to a particular method via its name using the special ::
syntax.
objectOrClass::methodName
BiFunction<Double, Double, Double> powFun = (a, b) -> Math.pow(a, b);
BiFunction<Double, Double, Double> powFun = Math::pow;
There are four types of method references in general:
reference to a static method
ClassName::staticMethodName
reference to a constructor
ClassName::new
reference to an instance method of an object
objectName::instanceMethodName
reference to an instance of an object of a particular type
ClassName::instanceMethodName
Static method
Function<String, Integer> parseInt = Integer::parseInt;
int n1 = parseInt.apply("143");
Constructor
class User {
String userId;
public User(String userId) {
this.userId = userId;
}
}
Function<String, User> userFun = User::new;
// same using lambda
Function<String, User> userFun = userId -> new User(userId);
User user = userFun.apply("john.doe");
Instance method of an object
List<String> tokens = List.of("1", "name", "desc");
Function<Integer, String> posFun = tokens::get;
// lambda equivalent
Function<Integer, String> posFun = index -> tokens.get(index);
posFun.apply(1); // $3 ==> "name"
See how tokens
variable is captured from the context.
Instance method of an object of a particular type This is another way of referring non-static methods
Function<Long, Double> converter = Long::doubleValue;
// or lambda
Function<Long, Double> converter = val -> val.doubleValue();
converter.apply(516L);