3.2.  Define and write functional interfaces

[Note]

Functional interfaces provide target types for lambda expressions and method references. Each functional interface has a single abstract method (SAM), called the functional method for that functional interface, to which the lambda expression's parameter and return types are matched or adapted.

Functional interfaces can provide a target type in multiple contexts, such as assignment context, method invocation, or cast context:

// Assignment context
Predicate<String> p = String::isEmpty;					
					

// Method invocation context
stream.filter(e -> e.getSize() > 10)...
					

// Cast context
stream.map((ToIntFunction) e -> e.getSize())...
					

All the existing single method interfaces like Runnable, Callable, Comparator, and ActionListener as of Java 8.0 (and later) are functional interfaces and lambdas can be used anywhere a single abstract method interface is used.

@FunctionalInterface annotation

Java 8.0 have introduced a new annotation @FunctionalInterface to mark an interface as such. It's basically to communicate intent but also allows the compiler to do some additional checks.

[Warning]

The @FunctionalInterface annotation is optional, it is a "hint" for Java compiler.

For example, this interface compiles successfully:

public interface MyInterface {
}
					

But when you indicate that it should be a functional interface:

// Compilation fails !
@FunctionalInterface
public interface MyInterface {
}
					

The compiler will raise an error as there is no abstract method. It says that "MyInterface is not a functional interface" as "no abstract method was found". It will also error if we try and add a second method:

// Compilation fails !
@FunctionalInterface
public interface MyInterface {
    void doIt();
    void doItNow();
}
					

Default methods in interfaces

Since Java 8.0 interfaces support default and public static methods. A default method is an instance method defined in an interface whose method header begins with the default keyword; it also provides a code body. Every class that implements the interface inherits the interface's default methods and can override them.

Since functional interface requires a single abstract method, this code will NOT compile, as it is invalid functional interface:

// Compilation fails !
@FunctionalInterface
public interface MyDefInterface {
    default void doIt() { /* cool implementation */ }
}
					

But this functional interface will compile successfully:

@FunctionalInterface
public interface MyDefInterface {
    default void doIt() { /* cool implementation */ }
    void doItNow(); // Single Abstract Method (SAM)
}
					

Static methods in interfaces

A static method is a method that's associated with the class in which it's defined, rather than with any object created from that class. Every instance of the class shares the static methods of the class. Java since release 8.0 also lets public static methods be defined in interfaces where they can assist default methods.

Like static methods in classes, you specify that a method definition in an interface is a static method with the static keyword at the beginning of the method signature. All method declarations in an interface, including static methods, are implicitly public, so you can omit the public modifier.

[Warning]

Since Java 9.0 onwards you can explicitly provide private modifier for static methods in interfaces.

When you implement an interface that contains a static method, the static method is still part of the interface and not part of the implementing class. For this reason, you cannot prefix the method with the class name. Instead, you must prefix the method with the interface name.

Since functional interface requires a single abstract method, this code will NOT compile, as it is invalid functional interface:

// Compilation fails !
@FunctionalInterface
public interface MyStatInterface {
    static void doIt() { /* cool implementation */ }
}
					

But this functional interface will compile successfully:

@FunctionalInterface
public interface MyStatInterface {
    static void doIt() { /* cool implementation */ }
    void doItNow();
}
					

Public methods of java.lang.Object in functional interfaces

If an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also DOES NOT count toward the interface's abstract method count since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.

For example, java.util.Comparator is a functional interface even though it declared two abstract methods:


package java.util;

@FunctionalInterface
public interface Comparator<T> {

    int compare(T o1, T o2); // This is SAM

    boolean equals(Object obj); // ignored as it's a public method from java.lang.Object

    ...
}

					

The reason is because one of these abstract methods - equals() -- has signature identical to public method in java.lang.Object class:

public class Object {
    ...
    public boolean equals(Object obj) {
        return (this == obj);
    }
    ...
}
					
					

This is NOT a funtional interface:

// Compilation fails !
@FunctionalInterface
interface MyInterface {
    int doIt(); 
    Object clone(); // not ignored, as it's not a public (but protected) method from java.lang.Object
}
					

Although only doIt() is an abstract method which is not part of the java.lang.Object, the interface is not functional, as clone() method is not public in the Object class:

public class Object {
    ...
    @HotSpotIntrinsicCandidate
    protected native Object clone() throws CloneNotSupportedException;
    ...
}
					

Professional hosting         Exam 1Z0-817: Upgrade OCP Java 6, 7 & 8 to Java SE 11 Developer Quiz     Exam 1Z0-810: Upgrade to Java SE 8 Programmer Quiz