Omówienie interfejsów BiConsumer, BiFunction, BinaryOperator #4
Spis treści
W Java 8 istnieje pokaźna grupa interfejsów funkcjonalnych, które pokrótce będziemy omawiać. Warto zapoznać się ze wszystkimi i wiedzieć, że takie istnieją. Okazuje się, że odpowiadają one na niemalże wszystkie potrzeby programistów i tworzenie swoich interfejsów może być zbędne.
Zacznijmy więc, bo długa droga przed nami 😉 Dzisiaj pierwsza część serii artykułów opisująca interfejsy: BiConsumer, BiFunction, BinaryOperator
BiConsumer
Na początek zajmiemy się tym interfejsem, który posiada także metodę default:
@FunctionalInterface
interface BiConsumer<T, U> {
//Przyjmuje dwa argumenty. Nie zwraca wartości
void accept(T t, U u);
// Wykonuje operację jedna po drugiej na przekazanych BiConsumer
default java.util.function.BiConsumer<T, U> andThen(java.util.function.BiConsumer<? super T, ? super U> after) {
Objects.requireNonNull(after);
return (l, r) -> {
accept(l, r);
after.accept(l, r);
};
}
}
Metoda accept
nic nie zwraca, ale przyjmuje dwa argumenty, na których możemy wykonać operacje. Obiektem oczywiście może być cokolwiek, w naszym przypadku będzie to Double
:
BiConsumer<Double, Double> biConsumer = (x, y) -> {
System.out.println("Dodawanie: " + (x + y));
};
biConsumer.accept(1.0, 2.0);
Wynik:
Dodawanie: 3.0
Metoda andThen
służy do wykonania operacji na kilku obiektach typu BiConsumer
. Może to wyglądać tak:
BiConsumer<Double, Double> biConsumer = (x, y) -> {
System.out.println("Dodawanie: " + (x + y));
};
BiConsumer<Double, Double> biConsumer1 = (x, y) -> {
System.out.println("Mnożenie: " + (x * y));
};
BiConsumer<Double, Double> biConsumer2 = (x, y) -> {
System.out.println("Dzielenie: " + (x / y));
};
biConsumer.andThen(biConsumer1).andThen(biConsumer2).accept(3.0, 2.0);
Wynik:
Dodawanie: 5.0 Mnożenie: 6.0 Dzielenie: 1.5
BiFunction
@FunctionalInterface
interface BiFunction<T, U, R> {
//Przyjmuje dwa argumenty. Zwraca wynik.
R apply(T t, U u);
// Wykonuje BiFunction a potem przetwarza wynik w Function
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
Różnica z poprzednim interfejsem jest taka, że tym razem otrzymujemy zwrócony wynik:
BiFunction<Double, Double, Double> biFunction = (x, y) -> {
return x + y;
};
System.out.println(biFunction.apply(1.0, 1.0));
Wynik:
2.0
W metodzie andThen
możemy przekazać obiekt typu Function
, który dodatkowo przetwarza nam wynik naszej lambdy z interfejsu BiFunction
. Liczby zostaną dodane za pomocą naszej lambdy, którą zdefiniowaliśmy wyżej, a potem jeszcze zostanie dodana do ich liczba PI.
Function<Double,Double> function = x -> x + Math.PI;
System.out.println(biFunction.andThen(function).apply(1.0,1.0));
Wynik:
5.141592653589793
BinaryOperator
/**
* Przyjmuje dwa argumenty tego samego typu i
* zwraca wynik tego samego typu
*/
@FunctionalInterface
interface BinaryOperator<T> extends BiFunction<T, T, T> {
//Zwraca mniejszy element zgodnie z przekazanym Comparatorem
public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}
//Zwraca większy element zgodnie z przekazanym Comparatorem
public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
}
}
BinaryOperator
rozszerza interfejs BiFunction
, który jest przedstawiony wyżej. Działanie tych interfejsów jest prawie takie same, z małą różnicą. BinaryOperator
działa tylko na jednym i takim samym typie. BinaryOperator
dodatkowo posiada dwie metody static
: minBy
oraz maxBy
.
Na początek metoda apply
:
BinaryOperator<Integer> binaryOperator = (x, y) -> x + y;
System.out.println(binaryOperator.apply(4,5));
Wynik:
9
Jeżeli chcemy użyć metody minBy
musimy zdefiniować Comparator
. W tym przypadku użyjemy Comparator
, który kryje się w klasie Integer
.
BinaryOperator<Integer> biByMin = BinaryOperator.minBy(Integer::compareTo);
System.out.println(biByMin.apply(5, 7));
Wynik:
5
Podobna sytuacja z maxBy
:
BinaryOperator<Integer> biByMax = BinaryOperator.maxBy(Integer::compareTo);
System.out.println(biByMax.apply(123123, 897797789));
Wynik:
897797789