Important changes in Java ☕ 8 ⇒ 11 (interfaces)

Featured image

Java 8 features were here for quite some time. To be honest I didn’t even spend much time with previous versions. I’m already from that generation of programmers that started with stream operations right away. Which means that filter, map, reduce is the way to go for me. Not to mention generics, enums and other Java 5 features. From my perspective, it was already there from the beginning. Before jumping into streams we need to understand how those functional things can work with non-functional Java. These additions are not part of the core language, and they are just utility package which means that Java is not purely functional. Obviously, that is not an obstacle, and it’s more than enough for our daily development.

What is Functional Interface?

Functional interface - is an interface that contains only one abstract method and has a @FunctionalInterface annotation. There is no need for it, but with that interface is more readable/informative, so use it! Also thanks to that IDE shows you type-check error when you want to create more than one abstract method.

Example of Functional interface from the standard library is Runnable.

// explicit
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

Next example watch() method which is implicitly abstract.

// implicit
@FunctionalInterface
public interface Film {
    public String watch(String str);
}

More about interfaces

Before Java 8 interfaces could only have abstract methods. Implementation of these interfaces were in separate classes. When you tried to add something new to the interface you had to provide an implementation of each class that has your interface. Java 8 introduced a concept of default methods and static methods which allows us to use interfaces with implementations. That construct provides backward compatibility so that existing interfaces can use lambda expression without implementing methods in the implementation classes. If we’re already at the interfaces there is also little thing added with Java 9. You can define private methods inside of interface.

interface Film {
    String watch(String str);

    default String preview(String str) { ... } // Java 8 default method can be overridden

    static String preview(String str) { ... }  // Java 8 static method cannot be overridden

    private String adultOnly() { ... }         // Java 9 private method
}

class FilmImpl implements Film {

    @Override
    public String watch(String str) {   // Only watch() is required to implement
        previewFilm();
        return "Rick and Morty";
    }

    private void previewFilm() {
        Film.preview("Rick and Morty"); // Java 8 static method
        
        FilmImpl film = new FilmImpl();
        film.preview("Rick and Morty"); // Java 8 default method
    }
}

Most important functional interfaces

Supplier<T> { T get() }

Doesn’t take any object just provides a new object.

Consumer<T> { void accept(T t) }

Accepts an object and doesn’t return anything.

BiConsumer<T, U> { void accept(T t, U u) }

Accepts two different (or not) object and doesn’t return anything.

Predicate<T> { boolean test(T t) }

Takes an object and returns a boolean.

BiPredicate<T, U> { boolean test(T t, U u) }

Takes two parameters and returns a boolean.

Function<T, R> { R apply(T t) }

Takes an object as a parameter and returns another object.

BiFunction<T, U, R> { R apply(T t, U u) }

This function has 3 generic types T, U, R. Takes two objects as a parameter and returns another object.