Престиж учителя
04.12.2019
Пять тенденций в украинском IT-образовании
04.12.2019
Показати всі

Розуміння лямбда — виразів у Java

Лямбда-вираз — це блок коду, який можна передавати для виконання. Це загальна особливість для деяких мов програмування, таких як Lisp, Python, Scala тощо. Але перед версією Java 8 ми не могли зробити те ж саме в Java. Якщо ми хочемо виконати блок коду, нам потрібно створити об’єкт і передати об’єкт як параметр методу. Починаючи з  Java 8, лямбда-вирази дозволяють нам розглядати функціональність як аргумент методу та передавати блок коду не створюючи при цьому об’єкт.

Це принципова відмінність від того, що було раніше, адже Java позиціонувалася як чисто об’єктно-орієнтована мова програмування. Ми не могли виконати код будь-де крім як у методах об’єктів або при ініціалізації полів об’єктів. Отже введення лямбда-виразів це відступ від чистого ООП. Java крім парадигми ООП стала підтримувати в деякій мірі функціональне програмування.

Покажемо потужність лямбда-виразів на прикладі сортування.

Наступний приклад показує, як використовувати компаратор для сортування масиву користувальницьких об’єктів спочатку без використання лямбда-виразів.

Припустимо є клас котів, який потрібно сортувати.

public class Cat {
    String name;
    int height;
    int weight;
     public Cat(String n, int s, int w) {
        this.name = n;
        this.height = s;
        this.weight = w;
    }
     public String toString() {
        return getName() + «: size=» + getHeight() + » weight=» + getWeight()
                + » \n»;
    }
    //setters and getters …
}
 

Щоб сортувати масив котів, ми можемо використовувати статичний метод sort класу Arrays:

(1) static <T> void sort(T[] a, Comparator<? super T> c)

Другий параметр — це компаратор, який визначає яким чином буде відсортовано масив об’єктів, тобто визначає стратегію сортування. Це типовий приклад використання патерну стратегія.

import java.util.Arrays;
import java.util.Comparator;
import java.util.stream.Stream;
 
 public class ArraysSort {
     public static void main(String[] args) {
        Cat d1 = new Cat(«Max», 2, 50);
        Cat d2 = new Cat(«Rocky», 1, 30);
        Cat d3 = new Cat(«Bear», 3, 40);
         Cat[] catArray = { d1, d2, d3 };
        printCats(catArray);
         Arrays.sort(catArray, new Comparator<Cat>() {
            @Override
            public int compare(Cat o1, Cat o2) {
                return o1.getWeight() — o2.getWeight();
            }
        });
        printCats(catArray);
    }
     public static void printCats(Cat[] cats) {
        System.out.println(«—Cat List—«);
        for (Cat d : cats)
            System.out.print(d);
        System.out.println();
    }
}

Тепер розглянемо аналогічний приклад з використанням лямбда-виразу.

У Java 8 ми можемо сортувати в одному простому рядку коду:

Arrays.sort(catArray, (Cat m, Cat n) -> m.getWeight() — n.getWeight());
print Cats(catArray);
 

Тут (Cat m, Cat n) -> m.getWeight() — n.getWeight()) — це лямбда-вираз

Java 8 забезпечує підтримку лямбда-виразів лише з функціональними інтерфейсами. Функціональний інтерфейс — це також нова особливість, представлена в Java 8. Будь-який інтерфейс з одним абстрактним методом називається функціональним інтерфейсом. Оскільки існує лише один абстрактний метод, не існує плутанини у застосуванні лямбда-виразу до цього методу.

А який функціональний інтерфейс використовується у нашому випадку сортування масиву? Звичайно, це інтерфейс Comparator, який має один абстрактний метод — compare. Саме він є другим параметром у опису методу sort класу Arrays (див. 1).

Синтаксис лямбда-виразу складається з наступного:
1. Список формальних параметрів у дужках, розділених комами. У даному випадку це (Cat m, Cat n)
2. Маркер стрілки ->
3. Тіло, яке складається з одного виразу або блоку висловлювань. У даному випадку це один єдиний вираз — m.getWeight() — n.getWeight());

Крім цього стандартного синтаксису лямбда-вираз можна записати іншими способами:

  • Якщо тип параметра можна вивести з контексту, то його можна опустити: (m,  n) -> m.getWeight() — n.getWeight())
  • Якщо код не можна записати в один рядок, він може бути записаний у фігурних дужках {}. Код тепер повинен явно містити оператор return.
  • Круглі дужки можуть бути опущені для лямбда-виразу з одним параметром, якщо його тип також опущений:

String[] arr = { «program», «creek», «is», «a», «java», «site» };
Stream<String> stream = Stream.of(arr);
stream.forEach(x -> System.out.println(x));

  • Попередній код також можна записати таким чином, використовуючи посилання на метод:

Stream<String> stream = Stream.of(arr);
stream.forEach(System.out::println);

  • Коли для функції не використовується жоден параметр, ми можемо також записати лямбда-вираз. Наприклад, це може виглядати наступним чином:

( ) -> {for(int i=0; i<10; i++) doSomthing();}

Коли ми пояснюємо, чому функціональне програмування настільки потужне в Java 8, слід підкреслити Stream API. Stream у Java 8 — це послідовність елементів, що підтримують послідовні та паралельні агрегатні операції. Використовуючи Stream, ми можемо просто передати блок коду до потоку і застосувати функцію до кожного елемента Stream.

Для сортування масиву котів вище, ми також можемо використовувати API Stream:

import java.util.stream.Stream;
 public class Java8WhyLambda {
    public static void main(String[] args) {
        // create an array of Cats
        Cat d1 = new Cat(«Max», 2, 50);
        Cat d2 = new Cat(«Rocky», 1, 30);
        Cat d3 = new Cat(«Bear», 3, 40);
        Cat[] catArray = { d1, d2, d3 };
         // use stream to sort
        Stream<Cat> catStream = Stream.of(catArray);
        Stream<Cat> sortedCatStream = catStream.sorted((Cat m, Cat n) -> Integer.compare(m.getHeight(), n.getHeight()));
         sortedCatStream.forEach(d -> System.out.print(d));
    }
}

Правда у даному випадку сам масив залишається невідсортованим, а сортується тільки stream.

Дмитро Красношапка,
старший викладач кафедри
комп’ютерних технологій

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *