Lambda Expressions
CS 240: Advanced Programming Concepts
Lambda Expression Overview
Lambda Expression Benefits
Lambda Expression Example
class StringLengthComparator implements Comparator<String> {
public int compare(String first, String second) {
return Integer.compare(first.length(), second.length());}
}
Arrays.sort(stringArray, new StringLengthComparator());
Arrays.sort(stringArray, (first, second) -> Integer.compare(first.length(), second.length()));
How Java Lambdas Work
How Java Lambdas Work
Lambda Expression Example Explained
Arrays.sort(stringArray, (first, second) -> Integer.compare(first.length(),
second.length()));
new Comparator<String>() {
public int compare(String first, String second) {
return Integer.compare(first.length(), second.length());
}
}
3. JVM invokes the sort method with the Comparator instance passed as the second parameter
Functional Interfaces
Functional Interfaces
Any interface with exactly one abstract method (can contain any number of static, default, and redeclared Object methods)
Lambda Syntax
Lambda Syntax
(Parameter List) -> Body
Parameter List
Comma separated list of formal parameters
Data types are optional and can be inferred unless ambiguous in callee
Parentheses are optional for a single parameter
Empty parentheses are required for an empty parameter list
->
Arrow token required between parameter list and body
Body
A single expression or a statement block
Return is inferred for a single expression
Statement blocks are enclosed in curly braces
Return must be explicitly specified (unless void) for statement blocks
Function/Lambda Variables
Creating Function (lambda) Variables
public class LambdaVariable {
Predicate<String> strLenPredicate = s -> s.length() > 10;
}
Creating APIs with Lambdas
Creating APIs with Lambdas
Lambdas allow the creation of powerful, elegant APIs in your own code:
public class StringSelectorExample {
public static List<String> select(Collection<String> strings, Predicate<String> selector) {
List<String> selectedStrings = new ArrayList<>();
for (String aString : strings) {
if (selector.test(aString)) {
selectedStrings.add(aString);
}
}
return selectedStrings;
}
}
Creating APIs with Lambdas (cont.)
Consider calling select with the lines of Robert Frost’s poem “The Road Not Taken”, and different selectors:
select(strings, x -> true)
Two roads diverged in a yellow wood,
And sorry I could not travel both
And be one traveler, long I stood
And looked down one as far as I could
To where it bent in the undergrowth;
Then took the other, as just as fair,
And having perhaps the better claim,
Because it was grassy and wanted wear;
...
public class StringSelectorExample {
public static List<String> select(
Collection<String> strings,
Predicate<String> selector) {
List<String> selectedStrings =
new ArrayList<>();
for (String aString : strings) {
if (selector.test(aString)) {
selectedStrings.add(aString);
}
}
return selectedStrings;
}
}
Creating APIs with Lambdas (cont.)
select(strings, x -> x.startsWith("I "))
I doubted if I should ever come back.
I shall be telling this with a sigh
I took the one less traveled by,
select(strings, x -> x.startsWith("And "))
And sorry I could not travel both
And be one traveler, long I stood
And looked down one as far as I could
And having perhaps the better claim,
And both that morning equally lay
And that has made all the difference.
select(strings, x -> x.contains("road"))
Two roads diverged in a yellow wood,
Two roads diverged in a wood, and I-
public class StringSelectorExample {
public static List<String> select(
Collection<String> strings,
Predicate<String> selector) {
List<String> selectedStrings =
new ArrayList<>();
for (String aString : strings) {
if (selector.test(aString)) {
selectedStrings.add(aString);
}
}
return selectedStrings;
}
}
Using Generic Interfaces Example Revisited
(with lambdas)
Using a Generic Interface (revisted)
public interface Function<T, R> {
R apply(T param);
}
public class Capitalizer implements
Function<String, String> {
@Override
public String apply(String param) {
return param == null ? null :
param.toUpperCase();
}
}
public class StringManipulator {
public String manipulateString(String str,
Function<String, String> manipulationFunction) {
return manipulationFunction.apply(str);
}
}
public static void main(String[] args) {
Function<String, String> cap = new Capitalizer();
var sm = new StringManipulator();
String s = sm.manipulateString(args[0], cap);
}
Using a Generic Interface (revisted)
public interface Function<T, R> {
R apply(T param);
}
public class Capitalizer implements
Function<String, String> {
@Override
public String apply(String param) {
return param == null ? null :
param.toUpperCase();
}
}
import java.util.functions.Function;
public class StringManipulator {
public String manipulateString(String str,
Function<String, String> manipulationFunction) {
return manipulationFunction.apply(str);
}
}
public static void main(String[] args) {
var sm = new StringManipulator();
String s = sm.manipulateString(args[0],
x -> x == null ? null : x.toUpperCase());
}
This is one of the 43 interfaces in the java.util.function package
This can be replaced with a lambda expression
Using a Generic Interface (revisted)
manipulateString("my string",
str -> str == null ? null : str.toUpperCase())
MY STRING
manipulateString("my string with spaces that will be removed",
str -> str == null ? null :
str.replaceAll(" ", ""))
mystringwithspacesthatwillberemoved
public class StringManipulator {
public String manipulateString(
String str,
Function<String, String> manipulationFunction) {
return manipulationFunction.apply(str);
}
}
Use of Lambdas in Existing Java Classes
Use of Lambdas in Existing Java Classes
Use of Lambda functions has been integrated throughout the Java API, especially in the collections API.
Example:
List<Integer> intList = new ArrayList<>();
intList.addAll(Arrays.asList(23, 5, 10, 71, 100, 1203, 4, 7, 748));
intList.removeIf(x -> x >= 100);
for(Integer value : intList) {
System.out.print(value + " ");
}
Prints: 23 5 10 71 4 7
Method References
Method References
Simplified syntax for lambda expressions that simply call an existing method
Consider a lambda expression that uses the Java forEach(Consumer<? super T>) method (defined in Iterable) to print the contents of a list:
List<Integer> intList = Arrays.asList(23, 5, 10, 71);
intList.forEach(x -> System.out.println(x));
The lambda expression simply calls an existing method, passing it’s parameter to the method, so it can be replaced with a method reference:
List<Integer> intList = Arrays.asList(23, 5, 10, 71);
intList.forEach(System.out::println);
Method References (cont.)