Functional Interface

Functional Interface

Functional interface is an interface having exactly one abstract method
called functional method to which the lambda expression’s parameter and return types are matched.
Functional interface provides target types for lambda expressions and method references.

Functional Interface rules

As discussed @FunctionalInterface is a runtime annotation
that is used to verify the interface follows all the rules
that can make this interface as functional interface.

  • Interface must have exactly one abstract method.
  • It can hava any number of default methods because they are not abstract and implementation is already provided by
    same.
  • Interface can declare an abstract method overriding one of the public method from java.lang.Object,
    that still can be considered as functional interface.
    The reason is any implementation class to this interface will have implementation for this abstract method either
    from super class(java.lang.Object) or defined by implementation class itself.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@FunctionalInterface
public interface FunctiionalInterface {
void say(String s);

@Override
String toString();//overriden from Object class

@Override
public boolean equals(Object obj);//overriden from Object class

default void beforeTask(){
System.out.println("before task...");
}

default void afterTask(){
System.out.println("after task...");
}
}

Predicate

java.util.function.Predicate has a boolean-valued function that takes an argument and return boolean value.

definition
1
2
3
public interface Predicate<T>{
boolean test(T t); // functional descriptor
}

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.IntStream;

public class PredicateTest {
public static void main(String[] args) {
Predicate<Integer> odd = num -> num % 2 != 0;
Predicate<Integer> positive = num -> num > 0;

Integer[] nums = IntStream.rangeClosed(-10, 10).boxed().toArray(Integer[]::new);

System.out.println("Odds:" + filter(nums, odd));
System.out.println("Positives:" + filter(nums, positive));
System.out.println("PositiveOdds:" + filter(nums, odd.and(positive)));
System.out.println("PositiveOrOdds:" + filter(nums, odd.or(positive)));
System.out.println("Evens:" + filter(nums, odd.negate()));
}

public static <T> List<T> filter(T[] array, Predicate<T> p) {
List<T> ret = new ArrayList<>();
for (T t : array) {
if (p.test(t)) {
ret.add(t);
}
}
return ret;
}
}

Output

1
2
3
4
5
Odds:[-9, -7, -5, -3, -1, 1, 3, 5, 7, 9]
Positives:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
PositiveOdds:[1, 3, 5, 7, 9]
PositiveOrOdds:[-9, -7, -5, -3, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Evens:[-10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10]
It has a couple of default methods which you can use it:
Method Description Example
and(Predicate<? super T> other Returns a composite predicate that represents logical` of two predicates(p1 AND p2) Predicate positiveOdd = odd.and(positive)
or(Predicate<? super T> other) Returns a composite predicate that represents logical OR of two predicates(p1 OR p2) Predicate positiveOrOdd = positive.or(odd)
negate() Returns a composite predicate that represents logical negation of this predicate Predicate negative = positive.negate()

Consumer

java.util.Consumer accepts an argument and returns no result.

definition
1
2
3
public interface Consumer<T>{
void accept(T t);
}

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.IntStream;

public class ConsumerTest {
public static void main(String[] args) {
Predicate<Integer> odd = p -> p % 2 == 0;
Integer[] nums = IntStream.rangeClosed(-10, 10).boxed().toArray(Integer[]::new);
// [-10, -8 .... 10]
List list = filter(nums, odd);

Consumer<List<Integer>> negateNum = l -> {
for (int i = 0; i < l.size(); i++) {
l.set(i, -l.get(i));
}
};

Consumer<List> showList = l -> {
l.forEach(System.out::println);
};

negateNum.andThen(showList).accept(list);
}

static <T> List<T> filter(T[] array, Predicate<T> p) {
List<T> list = new ArrayList<>();
for (T t : array) {
if (p.test(t)) {
list.add(t);
}
}
return list;
}
}

Output

1
2
3
4
5
6
7
8
9
10
11
10
8
6
4
2
0
-2
-4
-6
-8
-10

Consumer has also one default method called andThen(Consumer<? super T> after
which returns a composite consumer where second consumer will be executed after execution of first one.
If the first consumer throws any exception then the second consumer will not be executed.

Supplier

java.util.function.Supplier doesn’t accept any argument but returns a result.

definition
1
2
3
public interface Supplier<R>{
R get();
}

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.Random;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class SupplierTest {
public static void main(String[] args) {
Supplier<Long> randomId = () -> new Random().nextLong();
Supplier<UUID> uuid = () -> UUID.randomUUID();

Consumer<Trade> showMsg = t -> {
System.out.println("trade id:" + t.tradeId + ",trade location:" + t.location);
};

Trade trade = new Trade();
populate(trade, randomId);
showMsg.accept(trade);
populate(trade, uuid);
showMsg.accept(trade);

}

static <R> void populate(SupplierTest.Trade t, Supplier<R> supplier) {
t.tradeId = String.valueOf(supplier.get());
t.location = "Hub";
}

static class Trade {
String tradeId;
String location;
}
}

Function<T, R>

java.util.function.Function accepts an argument and return result.

definition
1
2
3
public interface Function<T, R>{
R apply(T t);
}

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.function.Function;

public class FunctionTeset {
public static void main(String[] args) {
Function<Integer, Double> half = n -> n / 2.0;

Function<Double, String> showMsg = d -> "value:" + d + ",type:" + d.getClass();
// half -> showMsg
System.out.println(half.andThen(showMsg).apply(520));

Function<String, Integer> mulTen = s -> Integer.parseInt(s) * 10;
// before <String> -> <Integer> -> <Double>
// mulTen -> half
System.out.println(half.compose(mulTen).apply("520"));

Function<String, String> itself = Function.identity();
//return it's input argument.
System.out.println(itself.apply("Identity of Function"));

}
}

Output

1
2
3
value:260.0,type:class java.lang.Double
2600.0
Identity of Function
Function has a couple of default ant static methods:
Method Description
default Function<V, R> compose(Function<? super V, ? extend T> before Returns a composed function that first applies the before function to its input,and then applies this function to the result
default Function<T, V> andThen(Function<? super R,? extend V> after Returns a compose function that first applies this function to its input,and then applies the after function to the result
static Function<T, T> identity() Returns a function that always return its input argument.Basically it is a helper method that used in Collector implementation
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2022-2023 Ataraxia

请我喝杯咖啡吧~