вторник, 27 февраля 2018 г.

Язык Котлин. Часть 2. Анонимные функции, лямбда-выражения и функции высшего порядка

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

Пример анонимной функции:

fun(x: Int, y: Int): Int { 
    return x + y
}

Функции с одной строкой кода в своём теле лучше записывать вообще в одну строку:

fun(x: Int, y: Int): Int = x + y

Использование анонимной функции:

operationSum(7, 8, fun(x: Int, y: Int): Int = x + y) 

Использование сокращённой записи анонимной функции в виде лямбда-выражения:

operationSum(7, 8, {x, y -> x + y}) 

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

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

В случае, если в лямбда-выражение передаётся только один параметр,  запись можно сократить ещё больше, убрав оператор -> и всё, что слева от него. В этом случае доступ к переданному параметру внутри тела лямбда-выражения осуществляется через автоматически созданную переменную it:

operationInc(7, {it + 1})

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

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

val sum = { x: Int, y: Int -> x + y }
val s = sum(2, 4)     // s будет равно 6

Помимо функций высшего порядка, лямбда-выражения можно передавать и в интерфейсы с одним абстрактным методом для автоматической реализации этого метода переданным кодом, если типы параметров передаваемой функции или лямбда-выражения совпадают с типами параметров абстрактного метода интерфеса. Пока это относится только к Java-интерфейсам, но, возможно, в будущем это будет относиться и к Котлин-интерфейсам. Реальный пример кода (использующего стандартную библиотеку архитектурных компонентов LiveData) с передачей лямбда-выражения в интерфейс наблюдателя за данными:

  /*
   * Подключение наблюдателя за изменениями данных.
   * Подлежащие наблюдению данные заключены в тип LiveData<t> и находятся в переменной этого типа leavDataList.
   * Для наблюдения за ними вызываем LiveData-метод observe, и передаём ему контекст владельца наблюдателя (это основная активность)
   * и реализацию интерфейса наблюдателя.
   */
  leavDataList.observe(this@MainActivity, Observer { newLeavDataList -> newLeavDataList?.let { recyclerViewAdapter.addItems(it) } })

В вышеприведённом примере мы реализуем интерфейс Observer, передав в него лямбда-выражение readoutsList -> readoutsList?.let { recyclerViewAdapter.addItems(it) }.

Интерфейс Observer имеет единственный абстрактный метод onChanged, принимающий данные любого типа (можно открыть Java-код интерфейса и посмотреть). Поэтому переданное интерфейсу лямбда-выражение автоматически преобразуются в реализацию единственного абстрактного метода интерфейса. Такое преобразование называется SAM-преобразованием (Single Abstract Method Conversions).

В лямбда-выражение передаются данные newLeavDataList. Они же оказываются на входе реализованного таким образом метода onChanged. Метод, в случае, если данные не null, добавляет их в адаптер. Кстати, для выполнения добавления только в том случае, если данные не null, реализовано с помощью метода данных let с использованием другого лямбда-выражения. О таком способе реализации null-безопасности рассказывалось в конце предыдущей части.

Комментариев нет:

Отправить комментарий