Interfaces

Overview

interface Foo {
    int foo();     // public abstract
    int FOO = 42;  // public static final
}
class FooImpl implements Foo {
    @Override
    public int foo() {  // must be public!
        return FOO;     // 42
    }
}

Default Methods in Interfaces

interface Foo {
    default int foo() {
        return 42;
    }
}
class FooImpl implements Foo {
    // no need to override foo!
    void bar() {
        Foo f = new FooImpl();
        f.foo(); // 42
    }
}

Default Method Conflicts

Examples

interface Foo {
    default int foo() {
        return 42;
    }
}
interface Bar {
    default int foo() {
        return -42;
    }
}
class FooBar implements Foo, Bar {
    public int foo() {
        return 0;
    }
    // It would not make any difference if either Foo.foo or Bar.foo was abstract
}
class FooBar implements Foo, Bar {
    public int foo() {
        return Foo.super.foo(); // super weird syntax!
    }
}
interface Foo {
    default int foo() {
        return 42;
    }
}
class Bar {
    public int foo() {
        return -42;
    }
}
class FooBar extends Bar implements Foo  {
    void fooBar() {
        foo(); // -42
    }
}

Static Methods in Interfaces

Calling a static method of a not-inherited interface

interface Bar {
    static void bar() {}
}

class Foo {
    void foo() {
        Bar.bar();  // No other way
    }
}

Calling a static method of an inherited interface

interface Foo {
    static void foo() {}
}

class FooImpl implements Foo {
    void fooTest() {
        // This is not allowed:
        // foo();
        
        // Correct way is:
        Foo.foo();  // No other way
    }
}

Inheritance in Static Methods in Interfaces

interface Foo {
    static void foo() {}
}

interface Bar extends Foo {}

// This will not compile:
// Bar.foo();
class Foo {
    static void foo() {}
}

class Bar extends Foo {}

// This compiles and runs fine:
Bar.foo();

Functional Interfaces

@FunctionalInterface
interface Foo {
    // single abstract method
    void foo();
    // equals method here is fine, it is inherited from the Object class 
    boolean equals(Object o);
}

class FooImpl implements Foo {
    @Override
    public void foo(){}
    // Implementing Foo does not mean equals method must be implemented
}

Functional Interfaces in JDK 8

Interface SAM Arguments Returns
Predicate test T boolean
BiPredicate test T, U boolean
Consumer accept T void
BiConsumer accept T, U void
Supplier get T
Function apply T R
UnaryOperator apply T T
BiFunction apply T, U R
BinaryOperator apply T, T T

1 Single Abstract Method

Primitive Specializations of Functional Interfaces in JDK 8

* Purely Primitive Interfaces are Printed in Bold

InterfaceSAMArgumentsReturns
Predicate
IntPredicatetestintboolean
LongPredicatetestlongboolean
DoublePredicatetestdoubleboolean
Consumer
IntConsumeracceptintvoid
LongConsumeracceptlongvoid
DoubleConsumeracceptdoublevoid
BiConsumer
ObjIntConsumeracceptT, intvoid
ObjLongConsumeracceptT, longvoid
ObjDoubleConsumeracceptT, doublevoid
Supplier
IntSuppliergetAsIntint
LongSuppliergetAsLonglong
DoubleSuppliergetAsDoubledouble
BooleanSuppliergetAsBooleanboolean
Function
IntFunctionapplyintR
LongFunctionapplylongR
DoubleFunctionapplydoubleR
ToIntFunctionapplyAsIntTint
ToLongFunctionapplyAsLongTlong
ToDoubleFunctionapplyAsDoubleTdouble
IntToDoubleFunctionapplyAsDoubleintdouble
IntToLongFunctionapplyAsLongintlong
LongToIntFunctionapplyAsIntlongint
LongToDoubleFunctionapplyAsDobulelongdouble
DoubleToIntFunctionapplyAsIntdoubleint
DoubleToLongFunctionapplyAsLongdoublelong
UnaryOperator
IntUnaryOperatorapplyAsIntintint
LongUnaryOperatorapplyAsLonglonglong
DoubleUnaryOperatorapplyAsDoubledoubledouble
BiFunction
ToIntBiFunctionapplyAsIntT, Uint
ToLongBiFunctionapplyAsLongT, Ulong
ToDoubleBiFunctionapplyAsDoubleT, Udouble
BinaryOperator
IntBinaryOperatorapplyAsIntint, intint
LongBinaryOperatorapplyAsLonglong, longlong
DoubleBinaryOperatorapplyAsDoubledouble, doubledouble

Predicate Default Methods

Predicates can be chained with and, or and negate as seen below.

List<String> s = new ArrayList<>(Arrays.asList("foo", "foof", "boo", "boob"));
Predicate<String> startsF = s -> s.toLowerCase().charAt(0) == 'f';
Predicate<String> endsF = s -> s.toLowerCase().charAt(s.length() - 1) == 'f';

s.removeIf(startsF.and(endsF).negate()); // [foof]

Chaining Consumers and Functions

You can not chain Consumers with BiConsumers, but you can chain Functions with BiFunctions.

// An example of chaining a BiFunction with a Function
BiFunction<String, Integer, String> biFunction = (s, integer) -> s + integer;
Function<String, String> function = s -> s.toLowerCase();
biFunction.andThen(function).apply("APPEND", 42); // append42

// An example of chaining Consumers
Consumer<StringBuilder> trimmer = s -> s.deleteCharAt(0);
Consumer<StringBuilder> doubler = s -> s.append(s.toString());
StringBuilder sb = new StringBuilder("kkoray");
trimmer.andThen(doubler).accept(sb); // koraykoray

Examples

BiPredicate Example

import java.util.function.BiPredicate;

class FooBar {
    static class Foo {int foo = 42;}
    static class Bar {int bar = 42;}
    public static <T, U> boolean foo(T t, U u, BiPredicate<T,U> biPredicate) {
        return biPredicate.test(t, u);
    }
    public static void main(String[] args) {
        foo(new Foo(), new Bar(), (foo, bar) -> foo.foo == 42 && bar.bar == 42);
        // true
    }
}

References


🏠