Главная страница
Навигация по странице:

  • Функциональные интерфейсы стандартной библиотеки Java

  • Consumer (потребитель)

  • Supplier (поставщик)

  • Predicate (предикат)

  • Function (функция)

  • BinaryOperator

  • UnaryOperator

  • лямбда выражения. Лямбда выражения. Лямбда выражения. Методы и конструкторы


    Скачать 42.42 Kb.
    НазваниеЛямбда выражения. Методы и конструкторы
    Анкорлямбда выражения
    Дата17.03.2023
    Размер42.42 Kb.
    Формат файлаdocx
    Имя файлаЛямбда выражения.docx
    ТипДокументы
    #996460

    Лямбда выражения. Методы и конструкторы

    Одним из преимуществ лямбд в java является то, что их можно передавать в качестве параметров в методы. Рассмотрим пример:

    public class LambdaApp {

    public static void main(String[] args) {

    Expression func = (n)-> n%2==0;

    int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    System.out.println(sum(nums, func)); // 20

    }

    private static int sum (int[] numbers, Expression func)

    {

    int result = 0;

    for(int i : numbers)

    {

    if (func.isEqual(i))

    result += i;

    }

    return result;

    }

    }

    interface Expression{

    boolean isEqual(int n);

    }

    Функциональный интерфейс Expression определяет метод isEqual(), который возвращает true, если в отношении числа n действует какое-нибудь равенство. В основном классе программы определяется метод sum(), который вычисляет сумму всех элементов массива, соответствующих некоторому условию. А само условие передается через параметр Expression func. Причем на момент написания метода sum мы можем абсолютно не знать, какое именно условие будет использоваться. Само же условие определяется в виде лямбда-выражения:

    Expression func = (n)-> n%2==0;

    То есть в данном случае все числа должны быть четными или остаток от их деления на 2 должен быть равен 0. Затем это лямбда-выражение передается в вызов метода sum. При этом можно не определять переменную интерфейса, а сразу передать в метод лямбда-выражение:

    int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    int x = sum(nums, (n)-> n > 5); // сумма чисел, которые больше 5

    System.out.println(x); // 30

    Начиная с JDK 8 в Java можно в качестве параметра в метод передавать ссылку на другой метод. Ссылка на метод передается в виде имя_класса::имя_статического_метода (если метод статический) или объект_класса::имя_метода (если метод нестатический).

    Ссылки на конструкторы название_класса::new.

    Ссылки на метод как параметры методов. Начиная с JDK 8 в Java можно в качестве параметра в метод передавать ссылку на другой метод. В принципе данный способ аналогичен передаче в метод лямбда-выражения.Ссылка на метод передается в виде имя_класса::имя_статического_метода (если метод статический) или объект_класса::имя_метода (если метод нестатический). Рассмотрим на примере:

    interface Expression{// функциональный интерфейс

    boolean isEqual(int n);

    }

    class ExpressionHelper{// класс, в котором определены методы

    static boolean isEven(int n){

    return n%2 == 0;

    }

    static boolean isPositive(int n){

    return n > 0;

    }

    }

    public class LambdaApp {

    public static void main(String[] args) {

    int[] nums = { -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5};

    System.out.println(sum(nums, ExpressionHelper::isEven));

    Expression expr = ExpressionHelper::isPositive;

    System.out.println(sum(nums, expr));

    }

    private static int sum (int[] numbers, Expression func) {

    int result = 0;

    for(int i : numbers) {

    if (func.isEqual(i)) { result += i; }

    return result;

    }

    }

    Здесь также определен функциональный интерфейс Expression, который имеет один метод. Кроме того, определен класс ExpressionHelper, который содержит два статических метода. В принципе их можно было определить и в основном классе программы, но я вынес их в отдельный класс.В основном классе программы LambdaApp определен метод sum(), который возвращает сумму элементов массива, соответствующих некоторому условию. Условие передается в виде объекта функционального интерфейса Expression. В методе main два раза вызываем метод sum, передавая в него один и тот же массив чисел, но разные условия. Первый вызов метода sum: System.out.println(sum(nums, ExpressionHelper::isEven));

    На место второго параметра передается ExpressionHelper::isEven, то есть ссылка на статический метод isEven() класса ExpressionHelper. При этом методы, на которые идет ссылка, должны совпадать по параметрам и результату с методом функционального интерфейса. При втором вызове метода sum отдельно создается объект Expression, который затем передается в метод:Expression expr = ExpressionHelper::isPositive; System.out.println(sum(nums, expr));

    Использование ссылок на методы в качестве параметров аналогично использованию лямбда-выражений. Если нам надо вызвать нестатические методы, то в ссылке вместо имени класса применяется имя объекта этого класса:

    interface Expression{ boolean isEqual(int n);}

    class ExpressionHelper{

    boolean isEven(int n){

    return n%2 == 0;

    }

    }

    public class LambdaApp {

    public static void main(String[] args) {

    int[] nums = { -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5};

    ExpressionHelper exprHelper = new ExpressionHelper();

    System.out.println(sum(nums, exprHelper::isEven)); // 0

    }

    private static int sum (int[] numbers, Expression func) {

    int result = 0;

    for(int i : numbers) {

    if (func.isEqual(i)) { result += i; }

    return result;

    }

    }

    Ссылки на конструкторы. Подобным образом мы можем использовать конструкторы: Например:

    public class LambdaApp {

    public static void main(String[] args) {



    UserBuilder userBuilder = User::new;

    User user = userBuilder.create("Tom");

    System.out.println(user.getName());

    }

    }

    interface UserBuilder{

    User create(String name);

    }

    class User{

    private String name;

    String getName(){

    return name;

    }

    User(String n){

    this.name=n;

    }

    }

    При использовании конструкторов методы функциональных интерфейсов должны принимать тот же список параметров, что и конструкторы класса, и должны возвращать объект данного класса. Более подробно про методы и конструкторы можно прочитать в статье “Лямбды как параметры и результаты методов”.

     При использовании конструкторов методы функциональных интерфейсов должны принимать тот же список параметров, что и конструкторы класса, и должны возвращать объект данного класса.

    Лямбды как результат методов

    Также метод в Java может возвращать лямбда-выражение. Рассмотрим следующий пример:

    interface Operation{

    int execute(int x, int y);

    }

    public class LambdaApp {

    public static void main(String[] args) {

    Operation func = action(1);

    int a = func.execute(6, 5);

    System.out.println(a); // 11

    int b = action(2).execute(8, 2);

    System.out.println(b); // 6

    }

    private static Operation action (int number){

    switch(number){

    case 1: return (x, y) -> x + y;

    case 2: return (x, y) -> x - y;

    case 3: return (x, y) -> x * y;

    default: return (x,y) -> 0;

    }

    }

    }

    В данном случае определен функциональный интерфейс Operation, в котором метод execute принимает два значения типа int и возвращает значение типа int. Метод action принимает в качестве параметра число и в зависимости от его значения возвращает то или иное лямбда-выражение. Оно может представлять либо сложение, либо вычитание, либо умножение, либо просто возвращает 0. Стоит учитывать, что формально возвращаемым типом метода action является интерфейс Operation, а возвращаемое лямбда-выражение должно соответствовать этому интерфейсу. В методе main мы можем вызвать этот метод action. Например, сначала получить его результат - лямбда-выражение, которое присваивается переменной Operation. А затем через метод execute выполнить это лямбда-выражение:

    Функциональные интерфейсы стандартной библиотеки Java

    Java представила поддержку функционального программирования в выпуске Java версии 8. Этот конкретный выпуск также представил несколько новых концепций, в частности лямбда-выражения, ссылки на методы и множество функциональных интерфейсов. При обсуждении последних есть несколько функциональных интерфейсов, а именно — Потребитель (Consumer), Поставщик (Supplier), Предикат (Predicat) и Функция (Function), которые являются наиболее важными.

    Consumer (потребитель)

    Consumer — это функциональный интерфейс, который принимает один параметр на вход и не возвращает никаких выходных данных. На языке непрофессионала, как следует из названия, реализация этого интерфейса потребляет вводимые данные. Пользовательский интерфейс имеет два метода.

    void accept(T t);

    default Consumer andThen(Consumersuper T> after);

     

    Метод accept является единым абстрактным методом (SAM), который принимает один аргумент типа T. Тогда как другой метод andThen является методом по умолчанию и используется для композиции.

    Ниже приведен пример интерфейса consumer. Мы создали потребительскую реализацию, которая использует строку, а затем просто выводит ее на экран. Метод forEach принимает реализацию потребительского интерфейса.

    public void whenNamesPresentConsumeAll() {

    Consumer printConsumer = t -> System.out.println(t);

    Stream cities = Stream.of("Sydney", "Dhaka", "New York", "London");

    cities.forEach(printConsumer);

    }

    Интерфейс Consumer имеет специфические типы реализаций для типов integer, double и long -> IntConsumer, DoubleConsumer и LongConsumer, как показано ниже:

    IntConsumer void accept(int x);

    DoubleConsumer void accept(double x);

    LongConsumer void accept(long x);

     

    Supplier (поставщик)

    Supplier — это простой интерфейс, указывающий, что данная реализация является поставщиком какого то результа. Этот интерфейс, однако, не накладывает никаких дополнительных ограничений, которые реализация поставщика должна возвращать при каждом новом получении результата.

    У поставщика есть только один метод get() и нет никаких других методов по умолчанию или статических методов.

    public void supplier() {

    Supplier doubleSupplier1 = () -> Math.random();

    DoubleSupplier doubleSupplier2 = Math::random;
    System.out.println(doubleSupplier1.get());

    System.out.println(doubleSupplier2.getAsDouble());

    }

    Одно из основных применений этого интерфейса это использование для включения отложенного выполнения. Это означает отсрочку выполнения до тех пор, пока оно не понадобится. Например, в классе Optional есть метод orElseGet. Этот метод срабатывает, если у option нет данных. Это показано ниже:

    public void supplierWithOptional() {

    Supplier doubleSupplier = () -> Math.random();

    Optional optionalDouble = Optional.empty();

    System.out.println(optionalDouble.orElseGet(doubleSupplier));

    }

     

    Predicate (предикат)

    Интерфейс Predicate представляет собой логическую функцию аргумента. Он в основном используется для фильтрации данных из потока (stream) Java. Метод фильтра потока принимает предикат для фильтрации данных и возврата нового потока, удовлетворяющего предикату. У предиката есть метод test(), который принимает аргумент и возвращает логическое значение.

    public void testPredicate() {

    List names = Arrays.asList("Smith", "Samueal", "Catley", "Sie");

    Predicate nameStartsWithS = str -> str.startsWith("S");

    names.stream().filter(nameStartsWithS).forEach(System.out::println);

    }

    Predicate Функциональный интерфейс Predicate проверяет соблюдение некоторого условия. Если оно соблюдается, то возвращается значение true. В качестве параметра лямбда-выражение принимает объект типа T:

    public interface Predicate {

    boolean test(T t);

    }

    import java.util.function.Predicate;

    public class LambdaApp {

    public static void main(String[] args) {

    Predicate isPositive = x -> x > 0;

    System.out.println(isPositive.test(5)); // true

    System.out.println(isPositive.test(-7)); // false

    }

    }

    Function (функция)

    Интерфейс Function — это более общий интерфейс, который принимает один аргумент и выдает результат. В нем применяется единый абстрактный метод (SAM), который принимает аргумент типа T и выдает результат типа R. Одним из распространенных вариантов использования этого интерфейса является метод Stream.map. Пример использования показан ниже:

    public void testFunctions() {

    List names = Arrays.asList("Smith", "Gourav", "John", "Catania");

    Function nameMappingFunction = String::length;

    List nameLength = names.stream()

    .map(nameMappingFunction).collect(Collectors.toList());

    System.out.println(nameLength);

    }
    Функциональный интерфейс Function представляет функцию перехода от объекта типа T к объекту типа R:

    public interface Function {

    R apply(T t);

    }

    import java.util.function.Function;

    public class LambdaApp {

    public static void main(String[] args) {

    Function convert = x-> String.valueOf(x) + " долларов";

    System.out.println(convert.apply(5)); // 5 долларов

    }

    }

     

    BinaryOperator

    BinaryOperator принимает в качестве параметра два объекта типа T, выполняет над ними бинарную операцию и возвращает ее результат также в виде объекта типа T:

    public interface BinaryOperator<T> {

    T apply(T t1, T t2);

    }

    import java.util.function.BinaryOperator;



    public class LambdaApp {

    public static void main(String[] args) {



    BinaryOperator multiply = (x, y) -> x*y;



    System.out.println(multiply.apply(3, 5)); // 15


    System.out.println(multiply.apply(10, -2)); // -20

    }

    }


    UnaryOperator

    UnaryOperator принимает в качестве параметра объект типа T, выполняет над ними операции и возвращает результат операций в виде объекта типа T:

    public interface UnaryOperator<T> {

    T apply(T t);

    }

    import java.util.function.UnaryOperator;

    public class LambdaApp {

    public static void main(String[] args) {

    UnaryOperator square = x -> x*x;

    System.out.println(square.apply(5)); // 25

    }

    }

    Более подробно об интерфейсах можно прочитать в статьях “Функциональные интерфейсы в Java 8 → Consumer, Supplier, Predicate и Function. Что к чему и зачем нужны” и “Встроенные функциональные интерфейсы”.

    В JDK 8 вместе с самой функциональностью лямбда-выражений также было добавлено некоторое количество встроенных функциональных интерфейсов, которые мы можем использовать в различных ситуациях и в различные API в рамках JDK 8. В частности, ряд далее рассматриваемых интерфейсов широко применяется в Stream API - новом прикладном интерфейсе для работы с данными. Рассмотрим основные из этих интерфейсов:

    Predicate

    Consumer

    Function

    Supplier

    UnaryOperator

    BinaryOperator

    BinaryOperator

    UnaryOperator

    Function

    1

    2

    3

    Consumer

    Consumer выполняет некоторое действие над объектом типа T, при этом ничего не возвращая:

    1

    2

    3

    public interface Consumer {

    void accept(T t);

    }

    Например:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    import java.util.function.Consumer;

    public class LambdaApp {

    public static void main(String[] args) {

    Consumer printer = x-> System.out.printf("%d долларов \n", x);

    printer.accept(600); // 600 долларов

    }

    }

    Supplier

    Supplier не принимает никаких аргументов, но должен возвращать объект типа T:

    1

    2

    3

    public interface Supplier {

    T get();

    }

    Например:

    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

    import java.util.Scanner;

    import java.util.function.Supplier;

    public class LambdaApp {

    public static void main(String[] args) {

    Supplier userFactory = ()->{

    Scanner in = new Scanner(System.in);

    System.out.println("Введите имя: ");

    String name = in.nextLine();

    return new User(name);

    };

    User user1 = userFactory.get();

    User user2 = userFactory.get();

    System.out.println("Имя user1: " + user1.getName());

    System.out.println("Имя user2: " + user2.getName());

    }

    }

    class User{

    private String name;

    String getName(){

    return name;

    }

    User(String n){

    this.name=n;

    }

    }

    Консольный вывод:


    написать администратору сайта