Что такое лямбда-выражения?

Ответ

Лямбда-выражения — это лаконичная запись для создания экземпляров функциональных интерфейсов (интерфейсов с одним абстрактным методом, например, Runnable, Comparator, Predicate). Они представляют собой блок кода, который можно передавать и выполнять.

Основной синтаксис:

  • (param) -> expression
  • (param1, param2) -> { statements; return value; }

Примеры в разных контекстах:

  1. Сортировка коллекции:

    List<String> list = Arrays.asList("z", "a", "c");
    // Лямбда как компаратор
    list.sort((s1, s2) -> s1.compareTo(s2));
  2. Обработка событий (GUI):

    button.addActionListener(event -> System.out.println("Кнопка нажата!"));
  3. Собственный функциональный интерфейс:

    @FunctionalInterface
    interface Calculator {
        int operate(int a, int b);
    }
    
    Calculator add = (x, y) -> x + y;
    Calculator multiply = (x, y) -> x * y;
    
    System.out.println(add.operate(5, 3));      // 8
    System.out.println(multiply.operate(5, 3)); // 15

Важные отличия от анонимных классов:

  • Краткость: Меньше шаблонного кода.
  • Производительность: Нет накладных расходов на создание нового класса (хотя может использовать invokedynamic).
  • Область видимости: Лямбда не интродуцирует новый уровень области видимости для имен переменных.

Ответ 18+ 🔞

Слушай, а вот эти лямбда-выражения в Java — это ж, блядь, просто песня, а не фича! Вместо того чтобы городить огород с анонимными классами на три экрана, ты пишешь пару символов и — вуаля, всё работает. Ёперный театр, удобно же!

Вот смотри, по сути, это просто короткий способ сделать экземпляр какого-нибудь функционального интерфейса. Это такой интерфейс, у которого один-единственный абстрактный метод, типа Runnable или Comparator. И лямбда — это и есть реализация этого метода, записанная в стиле «взял и написал».

Как это выглядит, эта ваша лаконичность:

  • (параметр) -> выражение
  • (парам1, парам2) -> { вот тут код; return чё-нибудь; }

Ну и давай живые примеры, а то занудствовать начнём:

  1. Сортировка, например. Раньше писали компаратор на полстраницы, а теперь:

    List<String> list = Arrays.asList("z", "a", "c");
    // Всё, блядь, сортировка готова. Красота!
    list.sort((s1, s2) -> s1.compareTo(s2));
  2. Кнопку нажали — событие обработали. Раньше слушателя накручивали, а теперь:

    button.addActionListener(event -> System.out.println("Кнопка нажата, ёпта!"));

    Чих-пых — и в продакшен!

  3. А если свой интерфейс функциональный захотел? Да пожалуйста!

    @FunctionalInterface
    interface Calculator {
        int operate(int a, int b); // Один метод — и ты король!
    }
    
    // И вот тебе две реализации, хоть складывай, хоть умножай
    Calculator add = (x, y) -> x + y;
    Calculator multiply = (x, y) -> x * y;
    
    System.out.println(add.operate(5, 3));      // 8
    System.out.println(multiply.operate(5, 3)); // 15

А чем это лучше старого доброго анонимного класса? Да всем, блядь!

  • Краткость. Не надо писать new Интерфейс() { public void method() {...}}. Просто стрелочка и всё.
  • Производительность. Там под капотом умные штуки, invokedynamic, не создаётся лишний класс просто так. Хотя, честно, иногда пофиг.
  • Область видимости. Вот это важно! Лямбда не создаёт свой новый мирок для переменных, как анонимный класс. Она видит всё вокруг, что в текущем методе происходит. Меньше геморроя, короче.

В общем, вещь. Сначала непривычно, а потом, как втянешься, обратно на анонимные классы смотреть не захочешь, как на лапти.