Intro

With interfaces like Comparator, we only have to implement a single abstract method, SAM for short. These interfaces are so important that they have several special names:

SAM Interfaces a.k.a. Functional Interfaces

Functional interface is interface with Single Abstract Method

If an interface has only one method that needs to be implemented, that interface can be implemented as a lambda expression. You don’t need to create a whole class to implement the interface; the compiler knows what the class and method would look like. What the compiler doesn’t know is the logic that goes inside that method.

Lambda implements Comparator

forEach() from Iterable interface

Remember List implements Iterable so it has this forEach() method, which takes a lambda expression. It is a way to pass a behaviour (follow these instructions) into a method, instead of passing an object containing data(“here is an object to be used”) like for loop.

아무거나

Using a method like forEach takes care of the “boilerplate,” the repetitive and common code like the for loop. Using forEach, passing in only the thing we want to do, can reduce accidental errors in our code.

Logic of lambda - passing behaviour method

We need to replace “do this” with a symbol that represents something to be passed into the method, not to represent that this code is gonna run straightaway. We use “->” arrow symbol.

We need values from somewhere right? We put the things the code need on the left side of the “do this symbol”.

forEach(item->System.out.println(item));

The lambda I passed in as method argument is used in the body of the method. But what exactly is lambda? How can the method just use this code that I passed it?

Lambda expressions are objects

Remember, everything in Java is an Object (well, except for the primitive types), and lambdas are no exception.

This means the reference to the lambda expression is going to be a Functional Interface. So, if you want your method to accept a lambda expression, you need to have a parameter whose type is a functional interface. That functional interface needs to be the right “shape” for your lambda.

Back to our imaginary forEach example; our parameter needs to implement a Functional Interface. We also need to call that lambda expression somehow, passing in the list element.

Remember, Functional Interfaces have a Single Abstract Method (SAM). It’s this method, whatever its name is, that gets called when we want to run the lambda code.

Shape of lambda expression

We’ve seen two lambda expressions that implement the Comparator interface: the example for sorting Lou’s songs in the previous chapter, and the lambda expression we passed into the sorted() stream operation in “Customizing the building blocks”

Return keyword in lambda expression is unnecessary

You don’t need it. If the lambda expression is a single line, and if the functional interface’s method signature requires a returned value, the compiler just assumes that your one line of code will generate the value that is to be returned.

Deeper anatomy of lambda expression

But let’s look at adding return keyword and the type of the parameter and observing lambda’s anatomy.

The shape of the lambda (its parameters, return type, and what it can reasonably be expected to do) is dictated by the Functional Interface it implements.

Lambda with multiple lines

3 things: Multiline lambda expressions must be inside curly braces. Then, like any other method code, every line must end in a semicolon, and if the method is supposed to return something, the lambda body must include the word “return” like any normal method.

But it is single line and you should make effort to make it 1 line, you can leave a lot of “boilerplate” syntax.

// no curly braces, no return, no semicolon at the end
(str1,str2)-> str2.length()-str1.length()

Number of parameters for lambda

How to know if a given method (like Stream’s method) takes a lambda?

By now you’ve seen that lambda expressions are implementations of a functional interface—that is, an Interface with a Single Abstract Method. That means the type of a lambda expression is this interface.

Go ahead and create a lambda expression. Instead of passing this into some method, as we have been doing so far, assign it to a variable. You’ll see it can be treated just like any other Object in Java, because everything in Java is an Object. The variable’s type is the Functional Interface.

Runnable runnable = () -> System.out.println("hey");

Consumer<String> consumer = str -> System.out.println(str);

Comparator<String> comparator = (s1,s2) -> s1.compareToIgnoreCase(s2);

How does this help us see if a method takes a lambda expression? Well, the method’s parameter type will be a Functional Interface. Take a look at some examples from the Streams API:

Spotting functional interfaces

Not all functional interfaces are conveniently tagged with @FunctionalInterface annotation, particularly in the older code. So let’s see how to spot them.

The only kind of methods allowed in interfaces were abstract methods. But from Java 8, they can also contain default and static methods.

Static methods, we saw in number matter (chapter 10) are methods that don’t belong to instance and are often used as helper methods.

Default methods are slightly different. Remember abstract classes from Chapter 8, Serious Polymorphism? They had abstract methods that need to be overridden, and standard methods with a body. On an interface, a default method works a bit like a standard method in an abstract class—they have a body, and will be inherited by subclasses.

Both default and static methods have a method body, with defined behavior. With interfaces, any method that is not defined as default or static is an abstract method that must be overridden.

Comparator has many methods, but only 1 SAM