Lambda expressions and method references
km -> km > 50 in step 1 is a lambda expression, or a lambda for short: a small method without a name, written right where it's used. filter calls it once for each value in the stream. The value goes into the parameter km, and the lambda's answer, true or false, says whether the value gets through.
The parts of a lambda
A lambda has a parameter list, an arrow ->, and a body:
- With one parameter, the parentheses around it may be left out:
km -> km > 50is the same as(km) -> km > 50. - The parameter's name is yours to pick, with one limit: no variable of that name may exist at that point of the method around the lambda. After
int km = 50;, the lambdakm -> km > 50giveserror: variable km is already defined in method main(String[]). A variable made inside a loop is gone once the loop ends, so after a loop that read intoString line, the lambdaline -> !line.isEmpty()is fine.x -> x > 50does the same thing askm -> km > 50, but a name likekmortitlesays what the value is. - There's no need to write the parameter's type. Java knows that a stream made from an
ArrayList<Integer>holds Integers.(Integer km) -> km > 50works too, but it's rarely written. - Two parameters need parentheses, with a comma between them:
(a, b) -> a + b. Step 7 uses this kind. - A short body is one expression, with no
returnand no semicolon: its value is the lambda's answer.
A bookshop keeps the titles on its "staff picks" table. The short titles, with forEach, which runs a lambda for each value that reaches it:
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<String> titles = new ArrayList<>();
titles.add("Beloved");
titles.add("Emma");
titles.add("Middlemarch");
titles.add("Dune");
titles.add("Persuasion");
titles.stream()
.filter(title -> title.length() <= 5)
.forEach(title -> System.out.println("Short title: " + title));
}
}
Short title: Emma
Short title: DuneforEach ends the stream, like count, but it gives back no value: its lambda does the work, here printing. So the statement isn't stored in a variable.
A body in braces
When a lambda needs more than one expression, its body goes in braces, as in a method: statements with semicolons, and a return for the answer, when the lambda must give one:
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<String> titles = new ArrayList<>();
titles.add("Beloved");
titles.add("Emma");
titles.add("Middlemarch");
titles.add("Dune");
titles.add("Persuasion");
titles.stream().filter(title -> {
String first = title.substring(0, 1);
return first.equals("D") || first.equals("E");
}).forEach(title -> {
System.out.println(title);
System.out.println(" " + title.length() + " letters");
});
}
}
Emma
4 letters
Dune
4 lettersThe lambda given to filter must answer true or false, so its block ends with return. The one given to forEach answers nothing, so it has no return, just like a void method. The } that closes a lambda's body is followed by the ) that closes the method call, which makes }) a common sight in stream code.
Method references
The lambda title -> System.out.println(title) only hands its value to another method. For that case Java has a shorter form, a method reference: System.out::println. Two colons stand between where the method is and its name, and there are no parentheses or parameters, since the stream gives the value:
ArrayList<String> titles = new ArrayList<>();
titles.add("Beloved");
titles.add("Emma");
titles.add("Dune");
titles.stream()
.filter(title -> title.length() == 4)
.forEach(System.out::println);
Emma
DuneThree method references you'll use in this module, and the lambdas they're short for:
System.out::printlnisvalue -> System.out.println(value).Integer::valueOfistext -> Integer.valueOf(text): it turns a String such as"42"into a number.String::toUpperCaseistext -> text.toUpperCase(): a method of the value itself, named by its class.
Step 4 puts String::toUpperCase to work, and step 5 Integer::valueOf. A method reference is never required: the lambda always works too. Use the reference when it reads more easily.
Variables from outside the lambda
A lambda can use the variables of the method it's in, such as a limit:
ArrayList<String> titles = new ArrayList<>();
titles.add("Beloved");
titles.add("Emma");
titles.add("Middlemarch");
int maxLength = 7;
titles.stream()
.filter(title -> title.length() <= maxLength)
.forEach(System.out::println);
Beloved
EmmaBut it can't change them. Counting with a variable of your own inside forEach doesn't compile:
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<String> titles = new ArrayList<>();
titles.add("Beloved");
titles.add("Emma");
int count = 0;
titles.stream().forEach(title -> count++);
System.out.println(count);
}
}
Main.java:9: error: local variables referenced from a lambda expression must be final or effectively final
titles.stream().forEach(title -> count++);
^
1 errorEffectively final means a variable that's never changed after it gets its value, such as maxLength above. A lambda may use only such variables of its method. That's no loss: the stream's way to count is filter and count, as in step 1, with no variable of your own.