Chapter 6 Lambdas and Functional Interfaces Flashcards

1
Q

Lambdas and Functional Interfaces

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

In Java 8, the language added the ability to write code using another style.
Functional programming is a way of writing code more declaratively.
You specify what you want to do rather than dealing with the state of objects.
You focus more on expressions than loops.
Functional programming uses lambda expressions to write code.

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

A lambda expression is a block of code that gets passed around.

You can think of a lambda expression as an unnamed method.
It has parameters and a body just like full-fledged methods do, but it doesn’t have a name like a real method.

Lambda expressions are often referred to as lambdas for short.

You might also know them as closures if Java isn’t your first language. If you had a bad experience with closures in the past, don’t worry. They are far simpler in Java.

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

Deferred execution means that code is specified now but will run later.

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

Lambdas work with interfaces that have only one abstract method.

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

Java relies on context when figuring out what lambda expressions mean.

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

The syntax of lambdas is tricky because many parts are optional.

These two lines do the exact same thing:
~~~
a -> a.canHop()

(Animal a) -> { return a.canHop(); }
~~~

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

Lambda syntax omitting optional parts
* A single parameter specified with the name a
* The arrow operator to separate the parameter and body
* A body that calls a single method and returns the result of that method

a -> a.canHop()
A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

example shows the most verbose form of a lambda that returns a boolean:
* 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

(Animal a) -> { return a.canHop(); }
A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

The parentheses can be omitted only if there is a single parameter and its type is not explicitly stated.

What is different here is that the rules change when you omit the braces.
Java doesn’t require you to type return or use a semicolon when no braces are used.

This special shortcut doesn’t work when we have two or more statements. At least this is consistent with using {} to create blocks of code elsewhere.

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

Here’s a fun fact: 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
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Valid Lambdas
~~~
() -> true // 0 parameter
a -> a.startsWith(“test”) //1 parameter, one parameter and doesn’t specify the type.
(String a) -> a.startsWith(“test”) //1 parameter
(a, b) -> a.startsWith(“test”) //2 parameters
((String a, String b) ->
a.startsWith(“test”)a, b) -> a.startsWith(“test”) //2 parameters
~~~

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

Invalid lambda
~~~
a, b -> a.startsWith(“test”) //Missing parentheses
a -> { a.startsWith(“test”); } //Missing return
a -> { return a.startsWith(“test”)
} //Missing semicolon
~~~

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

Functional Interfaces

A

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

functional interface has only one abstract method.
Your friend Sam can help you remember this because it is officially known as a Single Abstract Method (SAM) rule.

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

Single Abstract Method (SAM) rule.

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

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.

There are four functional interfaces you are likely to see on the exam. The next sections take a look at Predicate, Consumer, Supplier, and Comparator.

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

PREDICATE

A

It’s in the package java.util.function and the gist
of it is as follows:

public interface Predicate<T> {
boolean test(T t);
}
18
Q

CONSUMER

A

The Consumer functional interface has one method you need to know:
void accept(T t)

19
Q

SUPPLIER

A

The Supplier functional interface has only one method:
T get()

A good use case for a Supplier is when generating values.

20
Q

COMPARATOR

A

The Comparator rules.
A negative number means the first value is smaller,
zero means they are equal,
and a positive number means the first value is bigger.

The method signature is as follows:
int compare(T o1, T o2)

This interface is a functional interface since it has only one unimplemented method.
It has many static and default methods to facilitate writing complex comparators.

21
Q

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
22
Q

Can you figure out whether this sorts in ascending or descending order?

Comparator<Integer> ints = (i1, i2) -> i1 - i2;

A

The ints comparator uses natural sort order.

If the first number is bigger, it will return a positive number.

Try it. Suppose we are comparing 5 and 3.

The comparator subtracts 5-3 and gets 2. This is a positive number that means the first number is bigger and we are sorting in ascending order.

23
Q

Do you think these two statements would sort in ascending or descending order?

Comparator<String> strings = (s1, s2) -> s2.compareTo(s1);
Comparator<String> moreStrings = (s1, s2) -> - s1.compareTo(s2);
A

Both of these comparators actually do the same thing: sort in descending order. In the first example, the call to compareTo() is “backwards,” making it descending.

In the second example, the call uses the default order; however, it applies a negative sign to the result, which reverses it.

24
Q

Basic functional interfaces

A
  • Comparator, 2 parameters, return int
  • Consumer, 1 parameter, not return (void)
  • Predicate, 1 parameter, return boolean
  • Supplier, no parameter, type varies return
25
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.

26
Q

Variables in Lambdas
PARAMETER LIST

A
Predicate<String> p = x -> true;
Predicate<String> p = (var x) -> true;
Predicate<String> p = (String x) -> true;
27
Q

identify the type of the lambda parameter.
~~~
Predicate<String> p = x -> true;
Predicate<String> p = (var x) -> true;
Predicate<String> p = (String x) -> true;
~~~</String></String></String>

A

the answer is String.

How did we figure that out?
A lambda infers the types from the surrounding context. That means you get to do the same.

In this case, the lambda is being assigned to a Predicate that takes a String.

Another place to look for the type is in a method signature.

28
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);
}
~~~</Integer>

A

Integer

The whatAmI() method creates a lambda to be passed to the consume() method. Since the consume() method expects an Integer as the generic, we know that is what the
inferred type of x will be.

29
Q

LOCAL VARIABLES INSIDE THE LAMBDA BODY

A

While it is most common for a lambda body to be a single expression, it is legal to define a block. That block can have anything that is valid in a normal Java block, including local variable declarations. The following code does just that. It creates a local variable named c that is scoped to the lambda block.

(a, b) -> { int c = 0; return 5;}

30
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));
}
~~~</Integer>

A

Integer

Since we are sorting a list, we can use the type of the list to determine the type of the lambda parameter.

31
Q

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.
Now let’s try another one. Do you see what’s wrong here?

(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.

32
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 third syntax error is quite subtle and on line 16. See it? Look really closely.
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!

33
Q

VARIABLES REFERENCED FROM THE LAMBDA BODY

A

Lambda bodies are allowed to reference some variables from the surrounding code. 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);
    }
}
34
Q
2: public class Crow {
3:     private String color;
4:     public void caw(String name) {
5:         String volume = "loudly";
6:         name = "Caty";
7:         color = "black";
8:
9:     Consumer<String> consumer = s ->
10:     System.out.println(name + " says "
11:     + volume + " that she is " + color);
12:     volume = "softly";
13: }
A

In this example, name is not effectively final because it is set on line 6.

However, the compiler error occurs on line 10. It’s not a problem to assign a value to a nonfinal variable. However, once the lambda tries to use it, we do have a problem. The variable is no longer effectively final, so the lambda is not allowed to use the variable.

The variable volume is not effectively final either since it is updated on line 12. In this case, the compiler error is on line 11. That’s before the assignment! Again, the act of assigning a value is only a problem from the point of view of the lambda. Therefore, the lambda has to be the one to generate the compiler error.

35
Q

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

A
  • Instance variable. Allowed
  • Static variable Allowed
  • Local variable Allowed if effectively final
  • Method parameter Allowed if effectively final
  • Lambda parameter Allowed
36
Q

Calling APIs with Lambdas

A
37
Q

REMOVEIF()

A

List and Set declare a removeIf() method that takes a Predicate.

remove all of the bunny names that don’t begin with the letter h :
~~~
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]
~~~</String>

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!

38
Q

SORT()

A

While you can call Collections.sort(list), you can now sort directly on the list object.
~~~
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]
~~~
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. If you need a review of what the return value of a compare() operation means, check the Comparator section in this chapter or the Comparing section in Chapter 5. This is really important to memorize!
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.</String>

39
Q

FOREACH()

A

Our final method is forEach(). It takes a Consumer and calls that lambda for each element encountered.
~~~
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);
~~~
This code prints the following:
long ear
floppy
hoppy
[long ear, floppy, hoppy]</String>

The method on line 8 prints one entry per line. The method on line 9 prints the entire list on one line.

We can use forEach() with a Set or Map. For a Set, it works the same way as a List.
~~~
Set<String> bunnies = Set.of("long ear", "floppy", "hoppy");
bunnies.forEach(b -> System.out.println(b));
~~~
For a Map, you have to choose whether you want to go through the keys or values:
~~~
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));
~~~
It turns out the keySet() and values() methods each return a Set.
Since we know how to use forEach() with a Set, this is easy!</String>

40
Q

USING FOREACH() WITH A MAP DIRECTLY

A

You don’t need to know this for the exam, but 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.
~~~
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));
~~~