Лямбды

Лямбда — это короткая запись функции, которую можно передать как аргумент. До Java 8 чтобы передать «действие» в метод, нужно было написать анонимный класс — громоздко и многословно. Лямбда делает то же самое в одну строку.

// Раньше — анонимный класс
Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("Привет");
    }
};

// С лямбдой — то же самое
Runnable r = () -> System.out.println("Привет");

Синтаксис лямбды: (параметры) -> тело

() -> System.out.println("нет параметров")
x -> x * 2                          // один параметр — скобки необязательны
(x, y) -> x + y                     // два параметра
(String s) -> s.length() > 3        // с типом параметра
(x, y) -> { int r = x + y; return r; }  // несколько строк — блок с return

Method reference — ещё короче, когда лямбда просто вызывает существующий метод:

.map(name -> name.toUpperCase())  // лямбда
.map(String::toUpperCase)         // то же — method reference

.forEach(s -> System.out.println(s))
.forEach(System.out::println)     // то же

.sorted((a, b) -> a.compareTo(b))
.sorted(String::compareTo)        // то же
Копнуть глубже

Четыре вида method reference:

// 1. Статический метод
.map(Integer::parseInt)          // Integer.parseInt(s)

// 2. Метод экземпляра через тип (первый параметр становится получателем)
.map(String::toUpperCase)        // s.toUpperCase()

// 3. Метод конкретного экземпляра
String prefix = "Hello";
.map(prefix::concat)             // prefix.concat(s)

// 4. Конструктор
.map(User::new)                  // new User(s)

Захват переменных (closure). Лямбда может использовать переменные из окружающего контекста, но они должны быть effectively final — не меняться после присваивания (явный final не обязателен):

String greeting = "Привет";  // effectively final
names.forEach(name -> System.out.println(greeting + ", " + name));  // ✅

greeting = "Здравствуй";  // ❌ теперь не effectively final → не скомпилируется

Почему effectively final? Лямбда может выполниться в другом потоке или позже — если бы переменная менялась, поведение было бы непредсказуемым. JVM «захватывает» значение в момент создания лямбды, а не ссылку на переменную.

Лямбды не создают анонимный класс. В отличие от анонимных классов, лямбды реализованы через invokedynamic (JVM-инструкция) — это быстрее, меньше классов в памяти, и this внутри лямбды ссылается на объемлющий класс (а не на саму лямбду, как в анонимном классе).

🎤 Закрыл тему, если можешь объяснить:
• что такое лямбда и зачем она нужна — на примере сравнения с анонимным классом;
• четыре вида method reference и что такое effectively final (если дошёл до 2-го слоя).