Chapter 6 Lambdas and Functional Interfaces Notes Flashcards

1
Q

Lambdas and Functional Interfaces

A
  • 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.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Lambda syntax

A
// 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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

> [!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.

A
s -> {}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Valid?

() -> true
a -> a.startsWith("test")
(String a) -> a.startsWith("test")
(a, b) -> a.startsWith("test")
(String a, String b) -> a.startsWith("test")
A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Valid?

a, b -> a.startsWith("test")
a -> { a.startsWith("test"); }
a -> { return a.startsWith("test") }
A

Invalid

a, b -> a.startsWith("test") // Missing parenthese
a -> { a.startsWith("test"); } // Missing return 
a -> { return a.startsWith("test") } // Missing semicolon
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Functional Interfaces

A

Lambdas work with interfaces that have only one abstract method. These are called functional interfaces.

Single Abstract Method (SAM).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

> [!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.

A

@FunctionalInterface

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

There are four functional interfaces you are likely to see on the exam.

A

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)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

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: }
A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

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);
}
A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

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();
}
A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

> [!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.

A

Comparator interface is in java.util.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

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);
A
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
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Working with Variables in Lambdas

A

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!

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

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>

A

String

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

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);
}
A

Integer

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

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));
}
A

Integer

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

> [!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
19
Q
(a, b) -> { int a = 0; return 5;} // DOES NOT COMPILE
A
  • 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.
20
Q

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: }
A
  • 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!
21
Q

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);
    }
}
A
22
Q

Rules for accessing a variable from a lambda body
inside a method

A
  1. Instance variable. Allowed
  2. Static variable. Allowed
  3. Local variable. Allowed if effectively final
  4. Method parameter. Allowed if effectively final
  5. Lambda parameter. Allowed
22
Q

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);
    }
}
A
22
Q

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:

A
23
Q

Calling APIs with Lambdas

A
  1. removeIf()
    List and Set declare a removeIf() method that takes a Predicate.
  2. sort()
    List declare a sort() method that takes a Comparator
  3. forEach()
    List, Set and Map declare a forEach) method that takes a Consumer.
24
Q
3: List<String> bunnies = new ArrayList<>();
4: bunnies.add("long ear");
5: bunnies.add("floppy");
6: bunnies.add("hoppy");
7: System.out.println(bunnies); // [long ear, floppy, hoppy]
8: bunnies.removeIf(s -> s.charAt(0) != 'h');
9: System.out.println(bunnies); // [hoppy]
A
  • Line 8 takes care of everything for us. It defines a predicate that takes a String and returns a boolean. The removeIf() method does the rest.
  • The removeIf() method works the same way on a Set.
  • It removes any values in the set that match the Predicate.
  • There isn’t a removeIf() method on a Map. Remember that maps have both keys and values. It wouldn’t be clear what one was removing!
25
Q
3: List<String> bunnies = new ArrayList<>();
4: bunnies.add("long ear");
5: bunnies.add("floppy");
6: bunnies.add("hoppy");
7: System.out.println(bunnies); // [long ear, floppy, hoppy]
8: bunnies.sort((b1, b2) -> b1.compareTo(b2));
9: System.out.println(bunnies); // [floppy, hoppy, long ear]
A

On line 8, we sort the list alphabetically. The sort() method takes Comparator that provides the sort order. Remember that Comparator takes two parameters and returns an int.

There is not a sort method on Set or Map. Neither of those types has indexing, so it wouldn’t make sense to sort them.

26
Q
3: List<String> bunnies = new ArrayList<>();
4: bunnies.add("long ear");
5: bunnies.add("floppy");
6: bunnies.add("hoppy");
7:
8: bunnies.forEach(b -> System.out.println(b));
9: System.out.println(bunnies);
A

prints the following:

long ear
floppy
hoppy
[long ear, floppy, hoppy]
27
Q
Set<String> bunnies = Set.of("long ear", "floppy", "hoppy");
bunnies.forEach(b -> System.out.println(b));
A
28
Q
Map<String, Integer> bunnies = new HashMap<>();
bunnies.put("long ear", 3);
bunnies.put("floppy", 8);
bunnies.put("hoppy", 1);
bunnies.keySet().forEach(b -> System.out.println(b));
bunnies.values().forEach(b -> System.out.println(b));
A

For a Map, you have to choose whether you want to go through the keys or values:

29
Q
Map<String, Integer> bunnies = new HashMap<>();
bunnies.put("long ear", 3);
bunnies.put("floppy", 8);
bunnies.put("hoppy", 1);
bunnies.forEach((k, v) -> System.out.println(k + " " + v));
A

Java has a functional interface called BiConsumer. It works just like Consumer except it can take two parameters. This functional interface allows you to use forEach() with key/value pairs from Map.

30
Q

Using Method References

A

functional interface:

public interface LearnToSpeak {
     void speak(String sound);
}
public class DuckHelper {
    public static void teacher(String name, LearnToSpeak trainer) {
        // Exercise patience (omitted)
        trainer.speak(name);
    }
}

This code implements the functional interface using a lambda:

public class Duckling {
    public static void makeSound(String sound) {
        LearnToSpeak learner = s -> System.out.println(s);
        DuckHelper.teacher(sound, learner);
    }
}

A method reference lets us remove that redundancy and instead write this:

LearnToSpeak learner = System.out::println;
31
Q

> [!NOTE]
Remember that :: is like a lambda, and it is used for deferred execution with a functional interface.
You can even imagine the method reference as a lambda if it helps you.

A
32
Q

There are four formats for method references:

A
  • static methods
  • Instance methods on a particular object
  • Instance methods on a parameter to be determined at runtime
  • Constructors
33
Q

Method References Calling static Methods

A

functional interface that converts a double to a long:

interface Converter { 
    long round(double num);
}

We can implement this interface with the round() method in Math. Here we assign a method reference and a lambda to this functional interface:

14: Converter methodRef = Math::round;
15: Converter lambda = x -> Math.round(x);
16:
17: System.out.println(methodRef.round(100.1)); // 100

round() method is overloaded—it can take a double or a float.

With both lambdas and method references, Java infers information from the context. In this case, we said that we were declaring a Converter, which has a method taking a double parameter. Java looks for a method that matches that description. If it can’t find it or finds multiple matches, then the compiler will report an error. The latter is sometimes called an ambiguous type error.

34
Q

Method References Calling Instance Methods on a Particular Object

A

functional interface checks if a String starts with a specified value:

interface StringStart {
    boolean beginningCheck(String prefix);
}
18: var str = "Zoo";
19: StringStart methodRef = str::startsWith;
20: StringStart lambda = s -> str.startsWith(s);
21:
22: System.out.println(methodRef.beginningCheck("A")); // false
35
Q

Method References Calling Instance Methods on a Particular Object
functional interface with a method that doesn’t take any parameters

A

A method reference doesn’t have to take any parameters. In this example, we create a functional interface with a method that doesn’t take any parameters but returns a value:

interface StringChecker {
    boolean check();
}
18: var str = "";
19: StringChecker methodRef = str::isEmpty;
20: StringChecker lambda = () -> str.isEmpty();
21:
22: System.out.print(methodRef.check()); // true
36
Q

While all method references can be turned into lambdas, the opposite is not always true.

A
var str = "";
StringChecker lambda = () -> str.startsWith("Zoo");
StringChecker methodReference = str::startsWith; // DOES NOT COMPILE
        
StringChecker methodReference = str::startsWith("Zoo"); // DOES NOT COMPILE
37
Q

Method References Calling Instance Methods on a Parameter

A
interface StringParameterChecker {
    boolean check(String text);
}
23: StringParameterChecker methodRef = String::isEmpty;
24: StringParameterChecker lambda = s -> s.isEmpty();
25:
26: System.out.println(methodRef.check("Zoo")); // false

Line 23 says the method that we want to call is declared in String. It looks like a static method, but it isn’t. Instead, Java knows that isEmpty() is an instance method that does not take any parameters. Java uses the parameter supplied at runtime as the instance on which the method is called.

Compare lines 23 and 24 with lines 19 and 20 of our instance example. They look similar, although one references a local variable named str, while the other only references the functional interface parameters.

38
Q

Method References Calling Instance Methods on a Parameter
two parameters

A

functional interface that takes two parameters:

interface StringTwoParameterChecker {
    boolean check(String text, String prefix);
}
26: StringTwoParameterChecker methodRef = String::startsWith;
27: StringTwoParameterChecker lambda = (s, p) -> s.startsWith(p);
28:
29: System.out.println(methodRef.check("Zoo", "A")); // false

Since the functional interface takes two parameters, Java has to figure out what they represent. The first one will always be the instance of the object for instance methods. Any others are to be method parameters.

Remember that line 26 may look like a static method, but it is really a method reference declaring that the instance of the object will be specified later. Line 27 shows some of the power of a method reference. We were able to replace two lambda parameters this time.

39
Q

Method References Calling Constructors

A

functional interface:

interface EmptyStringCreator {
    String create();
}
30: EmptyStringCreator methodRef = String::new;
31: EmptyStringCreator lambda = () -> new String();
32:
33: var myString = methodRef.create();
34: System.out.println(myString.equals("Snake")); // false
40
Q

Method References Calling Constructors

A
interface StringCopier {
    String copy(String value);
}
32: StringCopier methodRef = String::new;
33: StringCopier lambda = x -> new String(x);
34:
35: var myString = methodRef.copy("Zebra");
36: System.out.println(myString.equals("Zebra")); // true

This means you can’t always determine which method can be called by looking at the method reference. Instead, you have to look at the context to see what parameters are used and if there is a return type. In this example, Java sees that we are passing a String parameter and calls the constructor of String that takes such a parameter.

41
Q

Reviewing Method References

A
  1. static method, ex: Math::random
  2. Instance methods on a particular object, ex: str::startsWith
  3. Instance methods on a parameter, ex: String::isEmpty
  4. Constructor, ex: String::new