Chapter 6 Lambdas and Functional Interfaces Notes Flashcards
Lambdas and Functional Interfaces
Java 8
introduce Functional programming- Functional programming is a way of writing code more declaratively.
- Functional programming uses lambda expressions to write code.
- A lambda expression is a block of code that gets passed around.
- You can think of a lambda expression as an unnamed method.
- In other words, a lambda expression is like a method that you can pass as if it were a variable.
Lambda syntax
// omitting optional parts a -> a.canHop() // including optional parts (Animal a) -> {return a.canHop();}
- A single parameter specified with the name a and stating the type is Animal
- The arrow operator to separate the parameter and body
- A body that has one or more lines of code, including a semicolon and a return statement
The parentheses can be omitted only if there is a single parameter and its type is not explicitly stated.
can omit braces when we have only a single statement.
Java doesn’t require you to type return or use a semicolon when no braces are used.
> [!NOTE:] s -> {}
is a valid lambda. If there is no code on the right side of the expression, you don’t need the semicolon or return statement.
s -> {}
Valid?
() -> true a -> a.startsWith("test") (String a) -> a.startsWith("test") (a, b) -> a.startsWith("test") (String a, String b) -> a.startsWith("test")
Valid lambdas
() -> true // 0 parameter a -> a.startsWith("test") // 1 parameter (String a) -> a.startsWith("test") // 1 parameter (a, b) -> a.startsWith("test") // 2 parameter (String a, String b) -> a.startsWith("test") // 2 parameter
The final two rows take two parameters and ignore one of them—there isn’t a rule that says you must use all defined parameters.
Valid?
a, b -> a.startsWith("test") a -> { a.startsWith("test"); } a -> { return a.startsWith("test") }
Invalid
a, b -> a.startsWith("test") // Missing parenthese a -> { a.startsWith("test"); } // Missing return a -> { return a.startsWith("test") } // Missing semicolon
Functional Interfaces
Lambdas work with interfaces that have only one abstract method. These are called functional interfaces.
Single Abstract Method (SAM).
> [!NOTE]
Java provides an annotation @FunctionalInterface on some, but not all, functional interfaces. This annotation means the authors of the interface promise it will be safe to use in a lambda in the future. However, just because you don’t see the annotation doesn’t mean it’s not a functional interface. Remember that having exactly one abstract method is what makes it a functional interface, not the annotation.
@FunctionalInterface
There are four functional interfaces you are likely to see on the exam.
It’s in the package java.util.function
1. Predicate
public interface Predicate<T> { boolean test(T t); }
2. Consumer
void accept(T t)
3. Supplier
T get()
4. Comparator (in java.util package)
int compare(T o1, T o2)
Predicate example:
1: import java.util.*; 2: import java.util.function.*; 3: public class PredicateSearch { 4: public static void main(String[] args) { 5: List<Animal> animals = new ArrayList<Animal>(); 6: animals.add(new Animal("fish", false, true)); 7: 8: print(animals, a -> a.canHop()); 9: } 10: private static void print(List<Animal> animals, 11: Predicate<Animal> checker) { 12: for (Animal animal : animals) { 13: if (checker.test(animal)) 14: System.out.print(animal + " "); 15: } 16: System.out.println(); 17: } 18: }
Let’s take a look at code that uses a Consumer:
public static void main(String[] args) { Consumer<String> consumer = x -> System.out.println(x); print(consumer, "Hello World"); } private static void print(Consumer<String> consumer, String value) { consumer.accept(value); }
Let’s take a look at code that uses a Supplier:
public static void main(String[] args) { Supplier<Integer> number = () -> 42; System.out.println(returnNumber(number)); } private static int returnNumber(Supplier<Integer> supplier) { return supplier.get(); }
> [!NOTE]
The Comparator interface existed prior to lambdas being added to Java. As a result, it is in a different package.
You can find Comparator in java.util.
Comparator interface is in java.util.
Sort in ascending or descending order?
Comparator<Integer> ints = (i1, i2) -> i1 - i2; Comparator<String> strings = (s1, s2) -> s2.compareTo(s1); Comparator<String> moreStrings = (s1, s2) -> - s1.compareTo(s2);
Comparator<Integer> ints = (i1, i2) -> i1 - i2; // ascending Comparator<String> strings = (s1, s2) -> s2.compareTo(s1); // descending. Comparator<String> moreStrings = (s1, s2) -> - s1.compareTo(s2); // ascending
Working with Variables in Lambdas
Variables can appear in three places with respect to lambdas: the parameter list, local variables declared inside the lambda body, and variables referenced from the lambda body. All three of these are opportunities for the exam to trick you. We will explore each one so you’ll be alert when tricks show up!
var can be used in place of the specific type.
identify the type of the lambda parameter.
all three of these statements are interchangeable:
~~~
Predicate<String> p = x -> true;
Predicate<String> p = (var x) -> true;
Predicate<String> p = (String x) -> true;
~~~</String></String></String>
String
Can you figure out the type of x?
public void whatAmI() { consume((var x) -> System.out.print(x), 123); } public void consume(Consumer<Integer> c, int num) { c.accept(num); }
Integer
What do you think the type of x is here?
public void counts(List<Integer> list) { list.sort((var x, var y) -> x.compareTo(y)); }
Integer
> [!NOTE]
When writing your own code, a lambda block with a local variable is a good hint that you should extract that code into a method.
(a, b) -> { int a = 0; return 5;} // DOES NOT COMPILE
- We tried to redeclare a, which is not allowed.
- Java doesn’t let you create a local variable with the same name as one already declared in that scope.
How many syntax errors do you see in this method?
11: public void variables(int a) { 12: int b = 1; 13: Predicate<Integer> p1 = a -> { 14: int b = 0; 15: int c = 0; 16: return b == c;} 17: }
- There are three syntax errors.
- The first is on line 13. The variable a was already used in this scope as a method parameter, so it cannot be reused.
- The next syntax error comes on line 14 where the code attempts to redeclare local variable b.
- The variable p1 is missing a semicolon at the end. There is a semicolon before the }, but that is inside the block. While you don’t normally have to look for missing semicolons, lambdas are tricky in this space, so beware!
The following code is legal:
The following code is legal:
public class Crow { private String color; public void caw(String name) { String volume = "loudly"; Consumer<String> consumer = s -> System.out.println(name + " says " + volume + " that she is " + color); } }
Rules for accessing a variable from a lambda body
inside a method
- Instance variable. Allowed
- Static variable. Allowed
- Local variable. Allowed if effectively final
- Method parameter. Allowed if effectively final
- Lambda parameter. Allowed
You can think of it as if we had written this:
public class Crow { private String color; public void caw(final String name) { final String volume = "loudly"; Consumer<String> consumer = s -> System.out.println(name + " says " + volume + " that she is " + color); } }
Method parameters and local variables are allowed to be referenced if they are effectively final.
This means that the value of a variable doesn’t change after it is set, regardless of whether it is explicitly marked as final
.
If you aren’t sure whether a variable is effectively final, add the final keyword. If the code would still compile, the variable is effectively final. You can think of it as if we had written this: