На этом шаге мы рассмотрим особенности использования таких функций.
А что, если вы хотите вывести строку "Madrigal has left the building" до и после вызова addEnthusiasm()?
Для этого нужно добавить в функцию easyPrint() возможность вызова в цепочке. Вы уже видели цепочки из вызовов функций: функции могут участвовать в цепочке, если они возвращают объект-приемник или иной объект, для которого можно вызывать последующие функции.
Обновите easyPrint() для вызова цепочкой и теперь попробуйте вызвать функцию easyPrint() дважды: до и после addEnthusiasm().
fun String.addEnthusiasm(amount: Int = 1) = this + "!".repeat(amount) fun Any.easyPrint(): Any { println(this) return this } fun main(args: Array<String>) { "Madrigal has left the building".easyPrint().addEnthusiasm().easyPrint() 42.easyPrint() }
Рис.1. Изменение easyPrint() и вызов easyPrint() дважды (Extensions.kt)
Код не скомпилировался. Первый вызов easyPrint() был разрешен, а addEnthusiasm() нет. Посмотрите на информацию о типе, чтобы понять, почему так происходит: щелкните на easyPrint() и нажмите Ctrl+Shift+P (Ctrl-P) и из появившегося списка расширений выберите первое: ("Madrigal has left the building".easyPrint()") (рисунок 2).
Рис.2. Получение информации о типе
Функция easyPrint() возвращает строку, для которой была вызвана, но для ее представления использует тип Any (рисунок 3).
Рис.3. Функция поддерживает вызов в цепочке, но возвращает тип, для которого нельзя вызвать addEnthusiasm()
Функция addEnthusiasm() доступна только для String, поэтому ее нельзя вызвать для значения, возвращаемого функцией easyPrint().
Чтобы решить эту проблему, можно сделать обобщенное расширение. Обновите функцию-расширение easyPrint() и используйте обобщенный тип в качестве принимающего вместо Any.
. . . . fun <T> T.easyPrint(): T { println(this) return this } . . . .
Рис.4. Обобщение easyPrint() (Extensions.kt)
Теперь, когда расширение использует параметр обобщенного типа Т в качестве приемника и возвращает Т вместо Any, информация о конкретном типе объекта-приемника передается далее по цепочке (рисунок 5).
Рис.5. Функция с поддержкой вызова в цепочке возвращает тип, который можно использовать
Попробуйте выполнить Extensions.kt снова. В этот раз строка будет выведена дважды:
Madrigal has left the building Madrigal has left the building! 42
Рис.6. Результат работы приложения
Ваша новая обобщенная функция-расширение работает с любым типом, а также обрабатывает информацию о нем. Расширения, использующие обобщенные типы, позволяют писать функции, которые могут работать с самыми разными типами в программе.
Расширения для обобщенных типов имеются и в стандартной библиотеке Kotlin. Например, посмотрите на объявление функции let:
public inline fun <T, R> T.let(block: (T) -> R): R { return block(this) }
Функция let() объявлена как обобщенная функция расширения, что позволяет ей работать со всеми типами. Она принимает лямбду, которая получает объект-приемник в качестве аргумента (Т) и возвращает значение некоторого нового типа R.
Обратите внимание на ключевое слово inline. Тот же совет, что мы давали раньше, применим и здесь: объявление функции-расширения встраиваемой, если она принимает лямбду, уменьшает затраты памяти.
На следующем шаге мы рассмотрим свойства-расширения.