На этом шаге мы сравним использование лямбд и ананимных внутренних классов.
Если до этого вы не использовали функциональные типы, то можете задаться вопросом: зачем их вообще использовать? Наш ответ: функциональные типы предлагают большую гибкость с меньшим объемом кода. Рассмотрим язык, который не поддерживает функциональные типы, например Java 8.
Java 8 поддерживает объектно-ориентированное программирование и лямбда-выражения, но не позволяет объявлять параметры или переменные, способные хранить другие функции. Вместо этого в нем предлагается использовать анонимные внутренние классы, то есть безымянные классы, объявляемые внутри другого класса ради реализации единственного метода. Анонимные встроенные классы можно передавать как экземпляры, подобно лямбдам. Например, в Java 8, чтобы просто передать один метод, вам придется написать:
Greeting greeting = (playerName, numBuildings) -> { int currentYear = 2021; System.out.println("Adding " + numBuildings + " houses"); return "Welcome to SimVillage, " + playerName + "! (copyright " + currentYear + ")"; }; public interface Greeting { String greet(String playerName, int numBuildings); } greeting.greet("Guyal", 6);
На первый взгляд выглядит почти эквивалентно тому, что предлагает Kotlin, - возможность передачи лямбда-выражения. Но если копнуть глубже, то становится ясно, что Java требует объявления именованных интерфейсов или классов для представления функции, определяющей лямбду, хотя экземпляры этих типов выглядят так, будто написаны с использованием того же сокращенного синтаксиса, доступного в Kotlin. Если вы просто захотите передать функцию без объявления интерфейса, то обнаружите, что Java не поддерживает такой лаконичный синтаксис.
Например, посмотрите на интерфейс Runnable в Java:
public interface Runnable { public abstract void run(); }
Объявление лямбды в Java 8 требует объявления интерфейса. В Kotlin такое дополнительное усилие для описания одного абстрактного метода не требуется. Следующий код на Kotlin эквивалентен коду на Java:
fun runMyRunnable(runnable: () -> Unit) = { runnable() } runMyRunnable { println("hey now") }
Объедините этот синтаксис с другими возможностями, изученными в предыдущих шагах, - встроенными функциями, ключевым словом it, замыканиями, - и вы получите более совершенный способ, чем объявление встроенных классов ради реализации единственного метода.
Такая гибкость Kotlin обеспечивается благодаря включению функций в число образцовых граждан, что освобождает вас от рутины и позволяет сосредоточиться на главном - на выполнении работы.
Со следующего шага мы начнем рассматривать null-безопасность и исключения.