{"id":"streams-lambdas","slug":"lambdas","title":"Lambda expressions and method references","text":"`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.\n\n### The parts of a lambda\n\nA lambda has a parameter list, an arrow `->`, and a body:\n\n- With one parameter, the parentheses around it may be left out: `km -> km > 50` is the same as `(km) -> km > 50`.\n- 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 lambda `km -> km > 50` gives `error: 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 into `String line`, the lambda `line -> !line.isEmpty()` is fine. `x -> x > 50` does the same thing as `km -> km > 50`, but a name like `km` or `title` says what the value is.\n- 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 > 50` works too, but it's rarely written.\n- Two parameters need parentheses, with a comma between them: `(a, b) -> a + b`. Step 7 uses this kind.\n- A short body is one expression, with no `return` and no semicolon: its value is the lambda's answer.\n\nA bookshop keeps the titles on its \"staff picks\" table. The short titles, with `forEach`, which runs a lambda for each value that reaches it:\n\n```java\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<String> titles = new ArrayList<>();\n        titles.add(\"Beloved\");\n        titles.add(\"Emma\");\n        titles.add(\"Middlemarch\");\n        titles.add(\"Dune\");\n        titles.add(\"Persuasion\");\n\n        titles.stream()\n                .filter(title -> title.length() <= 5)\n                .forEach(title -> System.out.println(\"Short title: \" + title));\n    }\n}\n```\n\n```output\nShort title: Emma\nShort title: Dune\n```\n\n`forEach` 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.\n\n### A body in braces\n\nWhen 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:\n\n```java\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<String> titles = new ArrayList<>();\n        titles.add(\"Beloved\");\n        titles.add(\"Emma\");\n        titles.add(\"Middlemarch\");\n        titles.add(\"Dune\");\n        titles.add(\"Persuasion\");\n\n        titles.stream().filter(title -> {\n            String first = title.substring(0, 1);\n            return first.equals(\"D\") || first.equals(\"E\");\n        }).forEach(title -> {\n            System.out.println(title);\n            System.out.println(\"  \" + title.length() + \" letters\");\n        });\n    }\n}\n```\n\n```output\nEmma\n  4 letters\nDune\n  4 letters\n```\n\nThe 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.\n\n### Method references\n\nThe 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:\n\n```java\nArrayList<String> titles = new ArrayList<>();\ntitles.add(\"Beloved\");\ntitles.add(\"Emma\");\ntitles.add(\"Dune\");\ntitles.stream()\n        .filter(title -> title.length() == 4)\n        .forEach(System.out::println);\n```\n\n```output\nEmma\nDune\n```\n\nThree method references you'll use in this module, and the lambdas they're short for:\n\n- `System.out::println` is `value -> System.out.println(value)`.\n- `Integer::valueOf` is `text -> Integer.valueOf(text)`: it turns a String such as `\"42\"` into a number.\n- `String::toUpperCase` is `text -> text.toUpperCase()`: a method of the value itself, named by its class.\n\nStep 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.\n\n### Variables from outside the lambda\n\nA lambda can use the variables of the method it's in, such as a limit:\n\n```java\nArrayList<String> titles = new ArrayList<>();\ntitles.add(\"Beloved\");\ntitles.add(\"Emma\");\ntitles.add(\"Middlemarch\");\nint maxLength = 7;\ntitles.stream()\n        .filter(title -> title.length() <= maxLength)\n        .forEach(System.out::println);\n```\n\n```output\nBeloved\nEmma\n```\n\nBut it can't change them. Counting with a variable of your own inside `forEach` doesn't compile:\n\n```java\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<String> titles = new ArrayList<>();\n        titles.add(\"Beloved\");\n        titles.add(\"Emma\");\n        int count = 0;\n        titles.stream().forEach(title -> count++);\n        System.out.println(count);\n    }\n}\n```\n\n```javac\nMain.java:9: error: local variables referenced from a lambda expression must be final or effectively final\n        titles.stream().forEach(title -> count++);\n                                         ^\n1 error\n```\n\n*Effectively 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.\n","task":"In `Main`, write the method `printLongTitles(ArrayList<String> titles, int limit)`. It prints, one per line, the titles that are longer than `limit` characters, in the list's order. Use a stream with `filter`, and end it with `forEach(System.out::println)`. The check runs:\n\n```java\nArrayList<String> titles = new ArrayList<>();\ntitles.add(\"Beloved\");\ntitles.add(\"Emma\");\ntitles.add(\"Middlemarch\");\ntitles.add(\"Dune\");\ntitles.add(\"Persuasion\");\nprintLongTitles(titles, 7);\n```\n\nand expects:\n\n```\nMiddlemarch\nPersuasion\n```\n","kind":"code","seed":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<String> titles = new ArrayList<>();\n        titles.add(\"Beloved\");\n        titles.add(\"Emma\");\n        titles.add(\"Middlemarch\");\n        titles.add(\"Dune\");\n        titles.add(\"Persuasion\");\n        printLongTitles(titles, 7);\n    }\n\n    // Write the method printLongTitles here.\n}\n","solution":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<String> titles = new ArrayList<>();\n        titles.add(\"Beloved\");\n        titles.add(\"Emma\");\n        titles.add(\"Middlemarch\");\n        titles.add(\"Dune\");\n        titles.add(\"Persuasion\");\n        printLongTitles(titles, 7);\n    }\n\n    public static void printLongTitles(ArrayList<String> titles, int limit) {\n        titles.stream()\n                .filter(title -> title.length() > limit)\n                .forEach(System.out::println);\n    }\n}\n","hints":["The header is `public static void printLongTitles(ArrayList<String> titles, int limit)`. Inside, the lambda may use `limit`, since the method never changes it.","`titles.stream().filter(title -> title.length() > limit).forEach(System.out::println);`"],"tests":[{"name":"Test 1","stdin":"","call":"ArrayList<String> titles = new ArrayList<>();\ntitles.add(\"Beloved\");\ntitles.add(\"Emma\");\ntitles.add(\"Middlemarch\");\ntitles.add(\"Dune\");\ntitles.add(\"Persuasion\");\nprintLongTitles(titles, 7);\n","expect":"Middlemarch\nPersuasion","hidden":false},{"name":"Test 2","stdin":"","call":"ArrayList<String> titles = new ArrayList<>();\ntitles.add(\"Emma\");\ntitles.add(\"Kindred\");\ntitles.add(\"Ivanhoe\");\ntitles.add(\"Emma\");\nprintLongTitles(titles, 3);\nSystem.out.println(\"--\");\nprintLongTitles(titles, 7);\nSystem.out.println(\"--\");\nprintLongTitles(titles, 6);\n","expect":"Emma\nKindred\nIvanhoe\nEmma\n--\n--\nKindred\nIvanhoe","hidden":true}],"require":[{"pattern":"\\bpublic\\s+static\\s+void\\s+printLongTitles\\s*\\(\\s*ArrayList<String>\\s+\\w+\\s*,\\s*int\\s+\\w+\\s*\\)","message":"Write the method as `public static void printLongTitles(ArrayList<String> titles, int limit)`."},{"pattern":"\\.filter\\s*\\(","message":"Keep the long titles with `filter`."},{"pattern":"\\.forEach\\s*\\(\\s*System\\s*\\.\\s*out\\s*::\\s*println\\s*\\)","message":"End the stream with `forEach(System.out::println)`."}],"forbid":[{"pattern":"\\b(for|while)\\b","message":"Use a stream this time, not a loop."}],"more":[{"task":"What does this program print?\n","kind":"predict","seed":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<String> titles = new ArrayList<>();\n        titles.add(\"Walden\");\n        titles.add(\"Hamlet\");\n        titles.add(\"Candide\");\n        titles.add(\"Ulysses\");\n\n        titles.stream().filter(title -> {\n            int length = title.length();\n            return length % 2 == 1;\n        }).forEach(title -> {\n            System.out.println(title.substring(0, 3));\n        });\n        titles.stream()\n                .filter(title -> title.startsWith(\"W\") || title.endsWith(\"t\"))\n                .forEach(System.out::println);\n    }\n}\n","solution":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<String> titles = new ArrayList<>();\n        titles.add(\"Walden\");\n        titles.add(\"Hamlet\");\n        titles.add(\"Candide\");\n        titles.add(\"Ulysses\");\n\n        titles.stream().filter(title -> {\n            int length = title.length();\n            return length % 2 == 1;\n        }).forEach(title -> {\n            System.out.println(title.substring(0, 3));\n        });\n        titles.stream()\n                .filter(title -> title.startsWith(\"W\") || title.endsWith(\"t\"))\n                .forEach(System.out::println);\n    }\n}\n","hints":["The first filter keeps the titles whose length is odd, and `substring(0, 3)` is a title's first three letters. The second stream starts again from the whole list, and `||` lets a title through when either test is true.","Walden and Hamlet have 6 letters, Candide and Ulysses have 7, so the first stream prints Can and Uly. Walden starts with W and Hamlet ends with t; Candide and Ulysses do neither."],"tests":[{"name":"Output","stdin":"","expect":"Can\nUly\nWalden\nHamlet","hidden":false}],"require":[],"forbid":[],"lines":["Can","Uly","Walden","Hamlet"]},{"task":"`countStartingWith` should count the titles that start with the given text, but it doesn't compile. Read javac's message, and rewrite the method's body with a stream that uses `filter` and `count`, with no variable of your own for the count. Keep the header as it is. The check runs:\n\n```java\nArrayList<String> titles = new ArrayList<>();\ntitles.add(\"The Hobbit\");\ntitles.add(\"Beloved\");\ntitles.add(\"The Road\");\ntitles.add(\"Theo\");\nSystem.out.println(countStartingWith(titles, \"The \"));\n```\n\nand expects:\n\n```\n2\n```\n","kind":"code","seed":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<String> titles = new ArrayList<>();\n        titles.add(\"The Hobbit\");\n        titles.add(\"Beloved\");\n        titles.add(\"The Road\");\n        titles.add(\"Theo\");\n        System.out.println(countStartingWith(titles, \"The \"));\n    }\n\n    public static long countStartingWith(ArrayList<String> titles, String start) {\n        long count = 0;\n        titles.stream().forEach(title -> {\n            if (title.startsWith(start)) {\n                count++;\n            }\n        });\n        return count;\n    }\n}\n","solution":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<String> titles = new ArrayList<>();\n        titles.add(\"The Hobbit\");\n        titles.add(\"Beloved\");\n        titles.add(\"The Road\");\n        titles.add(\"Theo\");\n        System.out.println(countStartingWith(titles, \"The \"));\n    }\n\n    public static long countStartingWith(ArrayList<String> titles, String start) {\n        return titles.stream()\n                .filter(title -> title.startsWith(start))\n                .count();\n    }\n}\n","hints":["javac says that `count` must be *effectively final*: a lambda can't change a variable of its method. The stream can count by itself.","Let `filter` keep the titles for which `title.startsWith(start)` is true, and return what `count()` gives: `return titles.stream().filter(title -> title.startsWith(start)).count();`"],"tests":[{"name":"Test 1","stdin":"","call":"ArrayList<String> titles = new ArrayList<>();\ntitles.add(\"The Hobbit\");\ntitles.add(\"Beloved\");\ntitles.add(\"The Road\");\ntitles.add(\"Theo\");\nSystem.out.println(countStartingWith(titles, \"The \"));\n","expect":"2","hidden":false},{"name":"Test 2","stdin":"","call":"ArrayList<String> titles = new ArrayList<>();\ntitles.add(\"Kindred\");\ntitles.add(\"Kim\");\ntitles.add(\"Beloved\");\nSystem.out.println(countStartingWith(titles, \"Ki\"));\nSystem.out.println(countStartingWith(titles, \"Z\"));\nSystem.out.println(countStartingWith(new ArrayList<>(), \"The\"));\n","expect":"2\n0\n0","hidden":true}],"require":[{"pattern":"\\.filter\\s*\\(","message":"Keep the matching titles with `filter`."},{"pattern":"\\.count\\s*\\(\\s*\\)","message":"Let the stream count with `count()`."}],"forbid":[{"pattern":"\\+\\+|\\+=","message":"Let the stream count: no `++` or `+=` of your own."},{"pattern":"\\b(for|while)\\b","message":"Use a stream this time, not a loop."}]}]}