- Функциональные интерфейсы
- Лямбда-выражения
- Встроенные функциональные интерфейсы
- Ссылки на методы
Если интерфейс содержит только один абстрактный метод, то н назвается функциональным.
Примеры:
- Интерфейс
Runnableялвяется функциональным, потому что он содержит только один методrun(). - Функциональный интерфейс
// Необязательная аннотация, помечающая, что интерфейс является функциональным
@FunctionalInterface
public interface MyInterface{
// один абстрактный метод
double getValue();
}В приведенном выше примере, интерфейс MyInterface имеет только один абстрактный метод getValue(). Значит, этот интерфейс — функциональный. 3. Функциональный интерфейс с применением дженериков
@FunctionalInterface
interface Function<T1, T2, R> {
// один абстрактный метод
R apply(T1 arg1, T2 arg2);
}Лямбда представляет собой набор инструкций, которые можно выделить в отдельную переменную и затем многократно вызвать в различных местах программы.
Основу лямбда-выражения составляет лямбда-оператор, который представляет стрелку ->. Этот оператор разделяет лямбда-выражение на две части: левая часть содержит список параметров выражения, а правая, собственно, представляет тело лямбда-выражения, где выполняются все действия.
Лямбда-выражение не выполняется само по себе, а образует реализацию метода, определенного в функциональном интерфейсе. При этом важно, что функциональный интерфейс должен содержать только один единственный метод без реализации.
Синтаксис
(параметры) -> {блок команд}Виды:
- Однострочные
() -> System.out.println("Hello world");
(int a, int b) -> { return a + b; }- Блочные (многострочные)
Обязательно наличие
return
() -> {
double pi = 3.1415;
return pi;
};Анонимный метод
List<String> names = Arrays.asList("peter", "anna", "bob", "mike", "ada");
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// o1.compareTo(o2); // a-z
// o2.compareTo(o1); // z-a
return o1.compareTo(o2);
}
});Лямбда
List<String> names = Arrays.asList("peter", "anna", "bob", "mike", "ada");
// b.compareTo(a) // z-a
// a.compareTo(b) // a-z
Collections.sort(names, (a, b) -> b.compareTo(a));Функциональный интерфейс
@FunctionalInterface
public interface ColorCheck {
boolean check(String color);
}Применение
public class FunctionalInterfaceColorCheck {
private int getColorNumber(String[] colors, ColorCheck checker) {
int result = 0;
for (String color : colors) {
if (checker.check(color)) {
result++;
}
}
return result;
}
public static void main(String[] args) {
String[] colors = {"blue", "blue", "red", "blue"};
FunctionalInterfaceColorCheck checker = new FunctionalInterfaceColorCheck();
// Использование анонимных методов
String[] moreColors = {"blue", "blue", "red", "blue", "yellow", "yellow", "green", "green", "green", "green"};
System.out.println("yellow = " + checker.getColorNumber(moreColors, new ColorCheck() {
@Override
public boolean check(String color) {
return color.equals("yellow");
}
}));
System.out.println("green = " + checker.getColorNumber(moreColors, new ColorCheck() {
@Override
public boolean check(String color) {
return color.equals("green");
}
}));
//применение лямбда-выражения
System.out.println("green = " + checker.getColorNumber(moreColors, color -> color.equals("green")));
System.out.println("yellow = " + checker.getColorNumber(moreColors, color -> color.equals("yellow")));
System.out.println("size less than 4 = " + checker.getColorNumber(moreColors, color -> color.length() < 4));
//применение лямбда-блока
System.out.println("got 2+ 'e' letters = " + checker.getColorNumber(moreColors, color -> {
int counter = 0;
for (int i = 0; i < color.length(); i++) {
if (color.charAt(i) == 'e') {
counter++;
}
}
return counter >= 2;
}));
}
}Лямбда выражения являются альтернативой анонимным классам. Но они не одинаковы.
Общее:
- ��окальные переменные могут быть использованы только если они final или effective final.
- Разрешается доступ к переменным класса и статическим переменным класса.
- Они не должны выбрасывать больше исключений чем определено в throws метода функционального интерфейса.
Различия:
- Для анонимных классов ключевое слово this ссылается на сам класс. Для лямбды выражений на внешний класс.
- Анонимные классы компилируются во внутренние классы. Но лямбда выражения преобразуются в статические private методы класса, в котором они используют invokedynamic инструкцию. Лямбда более эффективны, так как не надо загружать еще один класс.
В Java 8 добавлены встроенные функциональные интерфейсы в пакет java.util.function:
- Predicate (T -> boolean)
- Consumer
- Function
- Supplier
- UnaryOperator
Принимает на вход значение, проверяет состояние и возвращает boolean значение в качестве результата.
Predicate подтверждает какое-то значение как true или false.
Описание интерфейса Predicate:
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}public class PredicateDemo {
public static void main(String[] args) {
// Является ли число негативным
Predicate<Integer> negative = integer -> integer < 0;
System.out.println(negative.test(-6));
System.out.println(negative.test(6));
System.out.println(negative.test(0));
// Является ли строка пустой
System.out.println("\nisNull");
Predicate<String> isNull = s -> s == null;
System.out.println(isNull.test("Hello"));
System.out.println(isNull.test(""));
System.out.println(isNull.test(null));
}
}default Predicate<T> and(Predicate<? super T> other); // должны выполняться оба
default Predicate<T> or(Predicate<? super T> other); // хотя бы один
default Predicate<T> negate(); // отрицаниеМетод
andкомбинирует два предиката, но он оптимизирован для ��ыполнения короткого замыкания (short-circuiting). Это означает, что если первый предикат в цепочке вернет false, то второй предикат не будет выполнен, так как результат всей операции уже будет известен.
import java.util.function.Predicate;
public class PredicateDemo {
public static void main(String[] args) {
System.out.println("\nPredicate.and()");
Predicate<String> contatainsLetterA = s -> s.contains("a") || s.contains("A");
Predicate<String> contatainsLetterB = s -> s.contains("b") || s.contains("B");
// true + true = true
System.out.println(contatainsLetterA.and(contatainsLetterB).test("abcd"));
//true + false = false
System.out.println(contatainsLetterA.and(contatainsLetterB).test("acd"));
System.out.println("\nPredicate.negate()");
// true + !false = true
System.out.println(contatainsLetterA.and(contatainsLetterB.negate()).test("acd"));
}
}Consumer интерфейс используется в случае, если необходимо передать объект на вход и произвести над ним некоторые операции не возвращая результат.
Описание интерфейса Consumer:
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}Одно действие с объектом
public static void main(String[] args){
System.out.println("hw");
Consumer<String> printUpperCase=s->System.out.println(s.toUpperCase());
Consumer<String> printLowerCase=s->System.out.println(s.toLowerCase());
printUpperCase.accept("Hello World");
printLowerCase.accept("Hello World");
}Последовательность действий с объектом
public class ConsumerDemo {
public class Store {
public int productLeft;
public Store(int productLeft) {
this.productLeft = productLeft;
}
}
public static void main(String[] args) {
//Продажа товара
System.out.println("\nproduct test");
Consumer<Store> sellOneProduct = store -> {
System.out.println("product sold");
store.productLeft--;
System.out.println("product left = " + store.productLeft);
};
Store store = new ConsumerDemo().new Store(10);
sellOneProduct.accept(store);
sellOneProduct.accept(store);
sellOneProduct.accept(store);
}
}Consumer интерфейс содержит метод по умолчанию, который возвращает составной Consumer, выполняющий последовательно действия указанные в каждом интерфейсе:
default Consumer<T> andThen(Consumer<? super T> after)public static void main(String[] args) {
Consumer<String> printUpperCase = s -> System.out.println(s.toUpperCase());
Consumer<String> printLowerCase = s -> System.out.println(s.toLowerCase());
printUpperCase.andThen(printLowerCase).accept("Hello World");
}Принимает значение в качестве аргумента одного типа и возвращает другое значение. Часто используется для преобразования одного значения в другое:
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}public class FunctionDemo {
public static void main(String[] args) {
Function<Double, Long> doubleToLongConverter = d -> Math.round(d);
System.out.println(doubleToLongConverter.apply(5.23));
Function<Long, String> longStringFunction = l -> {
if (l < 0) {
return "Отрицательное число";
} else if (l > 0) {
return "Положительное число";
}
return "Ноль";
};
System.out.println(longStringFunction.apply(-1L));
System.out.println(longStringFunction.apply(1L));
System.out.println(longStringFunction.apply(0L));
}
}default <V> Function<T, V> andThen(Function<? super R, ? extends V> after);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before);Эти методы позволяют объединить несколько функций в одну цепочку, что может быть полезно при преобразовании данных.
- andThen() - выполняется после текущего интерфейса;
- compose() - выполняется перед текущим интерфейсом.
static <T> Function<T, T> identity() // всегда возвращает входной параметр.Интерфейс Supplier используется тогда, когда на вход не передаются значения, но необходимо вернуть результат.
public class SupplierDemo {
public static void main(String[] args) {
String str = "hello world";
Supplier<String> supplier = () -> str.toUpperCase();
System.out.println(supplier.get());
}
}Расширяет функционал функционального интерфейса Function. Используется в случае, если аргумент и возвращаемое значение одного типа.
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T>
{
…
}import java.util.function.UnaryOperator;
public class UnaryOperatorDemo {
public static void main(String[] args) {
UnaryOperator<String> uo = s -> s.toUpperCase();
// То же, что и Function<String, String> f = s -> s.toUpperCase();
System.out.print(uo.apply("Java 8"));
}
}Если лямбда выражения вызывают только один существующий метод, лучше ссылаться на этот метод по его имени
Consumer<String> consumer = str -> System.out.println(str);Можно переписать с помощью method references следующим образом:
Consumer<String> consumer = System.out::println;Ссылки на методы бывают четырех видов:
| Тип | Пример |
|---|---|
| Ссылка на статический метод | ContainingClass::staticMethodName |
| Ссылка на нестатический метод конкретного объекта | containingObject::instanceMethodName |
| Ссылка на нестатический метод любого объекта конкретного типа | ContainingType::methodName |
| Ссылка на конструктор | ClassName::new |