Use of default and static methods
A default method added to maintain the backward compatibility which allows older classes (without modifications) to access new version of an interface.
Java 9 interfaces can have private methods and private staic methods. These methods support code reusabilit in the interface level.
Introduction
For example, in this blog two interfaces are compared:
- java.util.Comparator
- java.util.function.Predicate
For example functional interface can be define as follows
@FunctionalInterface
interface F<T, R> {
R doIt(T t);
}
- only
default
,static
andprivate
methods are allowed - exactly one single abstract method available
@FunctionalInterface
is optional annotation to verify functional interface.
To use this functional interface:
public class LambdaTest {
public static void main(String[] args) {
F<Integer, Double> f = x -> x * 2.0;
System.out.println(f.doIt(10));
}
}
For example to create functional factorial lambda in Java 11:
UnaryOperator<Long> fact = n -> n == 0 ? 1 : n * fact.apply(n-1);
fact.apply(10l) // 3628800
Note: Please read the Java Streaming API recipes to find how to achieve the same factorial functionality using streams.
Another important interface is Comparator
:
List<String> list = Arrays.asList("Orange","Bannana","Apple","Kiwi")
//create lambda expression for the Comparator interface
Comparator<String> compare = (x, y) -> x.length - y.length
list.sort(compare) // [Kiwi, Apple, Orange, Bannana]
There are predefined functional interfaces to use:
# | Lambda Interface | Abstract Method |
---|---|---|
1 | Function<T,R> | R apply(T t) |
2 | UnaryOperator |
T apply(T t) |
3 | Predicate |
boolean test(T t) |
4 | Consumer |
void accept(T t) |
5 | Supplier |
T get() |
There are number of other slightly variations you can find in the java.util.function.*
pacakge.
For example, for the Consumer<T>
interface: DoubleConsumer
to pass double
, IntConsumer
to pass int
, LongConsumer
pass long
primitive types and BiConsumer<T,U>
.
BiConsumer<Integer, Double> f = (x,y) -> System.out.println(x*y);
f.accept(3,2.0); // 6.0
The functional interface DoubleFunction
example as follows:
// similar to Function<Double, String>
DoubleFunction<String> f = x -> Double.valueOf(x).toString();
f.apply(4.5); // "4.5"
// Double apply(String x, Integer y)
BiFunction<String, Integer, Double> f = (x,y) -> Double.parseDouble(x) * y;
f.apply("2.5",3); // 7.5
Variation of Predicate
:
DoublePredicate f = x -> x > 2.0;
f.test(2.1); // true
f.test(1.1); // false
This intro will help you to go through the next sections.
Methods in interfaces
Interfaces has evolved start from the Java 8 including following methods in addition to the abstract methods and public static final variables :
- static (Java 8)
- default (Java 8)
- Private (Java 9)
If the default method with same method signature defined in the super class and the interface, then super class takes the priority.
If the class implmements two interfaces with the same default method signature, then class has to override that default method with its own implementation.
Comparator Interface
Examples of default methods provided by the java.util.Comparator
interface:
thenComparing
adds additional comparatorsreversed
reverses sorting order
static methods provided by the Comparator interface:
nullsFirst
andnullsLast
return comparators that enable sorting collections with null values.
// create a simple string list
l1 = new ArrayList<>(){ {add("first");add("second");add("third");add("fourth");add("fifth");add("sixth");} };
Comparator<String> sortbyStr =(x, y) -> x.compareTo(y); // compare by string
Comparator<String> sortbyStrLength =(x, y) -> x.length() - y.length(); // compare by length of the string
// first sort by string then by length of the string and reversed the result
Collections.sort(l1, sortbyStr.thenComparing(sortbyStrLength).reversed())
// output is as fllows
// l1 ==> [third, sixth, second, fourth, first, fifth]
Notice line #7 where thenComparing
default method has been used.
Predicate Interface
Default methods provided by the java.util.function.Predicate interface:
and
combines predicateor
combines predicate operatornegate
returns a predicate that represents the logical negation of this predicate
Static methods provided by the Predicate interface:
not
returns a predicate that is the negation of the supplied predicateisEqual
returns a predicate that compares the supplied object with the contents of the collection
// create a simple string list
l1 = new ArrayList<>(){ {add("first");add("second");add("third");add("fourth");add("fifth");add("sixth");} };
// predicate to validate the instance type
Predicate<String> f1 = x -> x instanceof String
// predicate to filter out words length less than 5
Predicate<String> f2 = x -> x.length() > 5;
// use of default methods
l1.removeIf(f1.negate().or(f2));
// output is
// l1 ==> [first, third, fifth, sixth]
As shown in the line number #11, the words whose lengths are less than 5 will be remains in the list after apply.