Extension functions

Учимся добавлять методы к чужим типам, не меняя их код.

Extension function (функция-расширение) — функция, которая добавляет новый метод к уже существующему типу, не изменяя его исходный код и не наследуясь от него.

Иногда хочется добавить удобный метод к String или List, но менять их исходники нельзя. Kotlin решает это через функции-расширения — одну из самых любимых возможностей языка.

Объявление расширения

Перед именем функции указывают тип-получатель и точку. Внутри функции this ссылается на объект, у которого её вызвали.

fun String.shout(): String {
    return this.uppercase() + "!"
}

fun main() {
    println("привет".shout())
    println("kotlin".shout())
}

Вывод:

ПРИВЕТ!
KOTLIN!

Расширения для своих типов

Расширять можно любой тип, в том числе коллекции. Это позволяет писать выразительные «методы», как будто они встроены в тип.

fun List<Int>.secondOrNull(): Int? {
    return if (this.size >= 2) this[1] else null
}

fun main() {
    println(listOf(10, 20, 30).secondOrNull())
    println(listOf(5).secondOrNull())
}

Вывод:

20
null

Расширения-свойства

Аналогично методам можно добавлять и свойства-расширения — например, удобное вычисляемое свойство.

val String.firstWord: String
    get() = this.split(" ").first()

fun main() {
    println("Котлин очень крут".firstWord)
}

Вывод:

Котлин

Как работает под капотом

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

Частые ошибки

  • Ждать доступа к приватным полям типа. Расширение видит только публичный интерфейс.
  • Думать, что расширение «встраивается» в класс. Это статическая функция; сам класс не меняется.
  • Создавать расширение там, где хватает обычной функции. Расширение оправдано, когда читается естественнее как метод объекта.

Итог

  • Функция-расширение добавляет метод к существующему типу без его изменения.
  • Внутри расширения this — это объект-получатель.
  • Расширять можно и свои, и чужие типы, включая коллекции.
  • Под капотом это статическая функция без доступа к приватным членам.
Проверьте себя
1. Что позволяет сделать extension function?
AУнаследовать класс
BДобавить метод к существующему типу без изменения его кода
CСоздать синглтон
DУскорить программу
2. На что ссылается this внутри функции String.shout()?
AНа сам класс String
BНа объект-строку, у которого вызвали shout()
CНа null
DНа companion object
3. Имеет ли extension function доступ к приватным полям типа?
AДа, к любым полям
BНет, только к публичному интерфейсу
CТолько к статическим полям
DТолько при наследовании