Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Industrial programming >> java

Освоение Java Lambda-выражений: от основ к Stream API

Освоение Java Lambda-выражений

Java 8 представила лямбда-выражения для повышения выразительности языка, позволяя разработчикам писать более лаконичный и читаемый код.

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


Что такое функциональный интерфейс?

Функциональный интерфейс — это Java-интерфейс, содержащий ровно один абстрактный метод. Этот единственный метод определяет назначение интерфейса.

Классический пример — интерфейс Runnable из java.lang, предоставляющий только метод run().

Пример 1: определение функционального интерфейса

import java.lang.FunctionalInterface;

@FunctionalInterface
public interface MyInterface {
    // Единственный абстрактный метод
    double getValue();
}

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

В Java 7 функциональные интерфейсы назывались Single Abstract Methods (SAM) и обычно реализовывались через анонимные классы.

Пример 2: реализация SAM через анонимный класс

public class FunctionInterfaceTest {
    public static void main(String[] args) {
        // Анонимный класс, реализующий Runnable
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("I just implemented the Runnable Functional Interface.");
            }
        }).start();
    }
}

Вывод:

I just implemented the Runnable Functional Interface.

Хотя анонимные классы уменьшают шаблонный код по сравнению с традиционными реализациями, синтаксис остаётся громоздким. Лямбда-выражения устраняют необходимость явных имён методов при передаче функциональных интерфейсов как аргументов.


Введение в лямбда-выражения

Лямбда-выражение — это по сути анонимный метод, предоставляющий лаконичную реализацию абстрактного метода функционального интерфейса. Оно не выполняется независимо; вместо этого оно поставляет поведение для метода интерфейса.

Как определить лямбда-выражение в Java

Общий синтаксис:

(parameter list) -> lambda body

Оператор стрелки (->) разделяет список параметров и тело. Конкретные примеры ниже.

Предположим, метод возвращает Пи:

double getPiValue() { return 3.1415; }

С лямбдой:

() -> 3.1415

Без параметров левая часть стрелки пуста, а правая — тело выражения, возвращающее значение.


Типы тел лямбда-выражений

Тела лямбд в Java бывают двух форм:

1. Тело-выражение (Expression Body) — содержит одно выражение.

() -> System.out.println("Lambdas are great");

2. Блочное тело (Block Body) — заключено в фигурные скобки, позволяет множественные операторы и опциональный return.

() -> { double pi = 3.1415; return pi; };

Примечание: Тела-выражения не требуют явного return, тогда как блочные тела требуют его, если возвращается значение.


Пример 3: лямбда-выражение в практике

Напишем программу, возвращающую Пи через лямбда-выражение.

import java.lang.FunctionalInterface;

@FunctionalInterface
interface MyInterface {
    double getPiValue();
}

public class Main {
    public static void main(String[] args) {
        MyInterface ref = () -> 3.1415;
        System.out.println("Value of Pi = " + ref.getPiValue());
    }
}

Вывод:

Value of Pi = 3.1415

Ключевые выводы:


Лямбда-выражения с параметрами

Как методы, лямбды могут принимать параметры. Пример:

(n) -> (n % 2) == 0

Эта лямбда проверяет, является ли входное целое чётным.

Пример 4: реверс строки с параметризованной лямбдой

@FunctionalInterface
interface MyInterface {
    String reverse(String s);
}

public class Main {
    public static void main(String[] args) {
        MyInterface ref = (str) -> {
            String result = "";
            for (int i = str.length() - 1; i >= 0; i--) {
                result += str.charAt(i);
            }
            return result;
        };
        System.out.println("Lambda reversed = " + ref.reverse("Lambda"));
    }
}

Вывод:

Lambda reversed = adbmaL

Обобщённый функциональный интерфейс

Функциональные интерфейсы могут быть обобщёнными, позволяя работать с любым типом данных. Например:

@FunctionalInterface
interface MyInterface<T> {
    T func(T t);
}

Пример 5: обобщённые лямбды для строк и целых чисел

public class Main {
    public static void main(String[] args) {
        // Реверс строки
        MyInterface<String> reverse = (str) -> {
            String result = "";
            for (int i = str.length() - 1; i >= 0; i--) {
                result += str.charAt(i);
            }
            return result;
        };
        System.out.println("Lambda reversed = " + reverse.func("Lambda"));
        // Факториал целого
        MyInterface<Integer> factorial = (n) -> {
            int result = 1;
            for (int i = 1; i <= n; i++) {
                result *= i;
            }
            return result;
        };
        System.out.println("factorial of 5 = " + factorial.func(5));
    }
}

Вывод:

Lambda reversed = adbmaL
factorial of 5 = 120

Лямбда-выражения и Stream API

Пакет java.util.stream, введённый в JDK 8, даёт разработчикам возможность выполнять функциональные операции над коллекциями: фильтрацию, отображение, редукцию, сортировку и многое другое.

Рассмотрим список названий мест, каждое с префиксом страны. Используя стримы и лямбды, можно эффективно извлекать и манипулировать данными.

Пример 6: обработка потоков с лямбдами

import java.util.ArrayList;
import java.util.List;

public class StreamMain {
    static List<String> places = new ArrayList<>();

    public static List<String> getPlaces() {
        places.add("Nepa, Kathmandu");
        places.add("Nepa, Pokhara");
        places.add("India, Delhi");
        places.add("USA, New York");
        places.add("Africa, Nigeria");
        return places;
    }

    public static void main(String[] args) {
        List<String> myPlaces = getPlaces();
        System.out.println("Места из Непала:");
        myPlaces.stream()
            .filter(p -> p.startsWith("Nepa"))
            .map(p -> p.toUpperCase())
            .sorted()
            .forEach(p -> System.out.println(p));
    }
}

Вывод:

Места из Непала:
NEPA, KATHMANDU
NEPA, POKHARA

Здесь цепочка стрим-операций — filter, map, sorted, forEach — каждая принимает лямбду, что драматически сокращает шаблонный код по сравнению с pre-Java-8 решениями.

java

  1. Освоение интерфейсов в C#: определение, реализация и практические примеры
  2. Освоение операторов Java: типы, синтаксис и практические примеры
  3. Понимание выражений, операторов и блоков в Java
  4. Интерфейсы в Java: концепции, реализация и лучшие практики
  5. Try-with-resources в Java: автоматическое управление ресурсами
  6. Аннотации в Java: типы, размещение и практические примеры
  7. Интерфейс vs Абстрактный класс в Java: Как выбрать правильную абстракцию
  8. Регулярные выражения в Java: Мастерство сопоставления шаблонов с java.util.regex
  9. Интерфейсы в Java: определение, использование и лучшие практики
  10. Java 9 Private Interface Methods: инкапсуляция и повторное использование кода