Как объявить и использовать extension-функции?

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

Синтаксис объявления

fun <Имя_типа>.<Имя_функции>() { ... }

Ключевой момент: перед именем функции пишется тип, который она будет расширять — это и делает функцию extension.

Пример:

fun String.addBrackets(): String {
return "\[$this\]"
}

Теперь можно вызывать .addBrackets() у любой строки:

val name = "Alex".addBrackets() // \[Alex\]

Как это работает

  • this в теле функции — это ссылка на экземпляр объекта, для которого вызывается расширение.

  • Extension-функции не изменяют сам класс — они синтаксический сахар. Компилятор преобразует вызов в обычный статический вызов с передачей объекта как аргумента.

val name = "Elvis".addBrackets()
// превращается в: ExtensionKt.addBrackets("Elvis")

Примеры

Расширение стандартного типа

fun Int.square(): Int {
return this \* this
}
val result = 5.square() // 25

Расширение nullable-типа

fun String?.orAnonymous(): String {
return this ?: "Anonymous"
}
val userName: String? = null
println(userName.orAnonymous()) // Anonymous

Использование с классами

class Person(val name: String)
fun Person.sayHello() {
println("Привет, меня зовут $name")
}
val p = Person("Иван")
p.sayHello() // Привет, меня зовут Иван

Организация и импорт

Extension-функции можно определять в любом файле, и потом импортировать:

// File: utils/StringUtils.kt
fun String.trimAndCapitalize(): String {
return this.trim().replaceFirstChar { it.uppercase() }
}

Использование:

import utils.trimAndCapitalize
val s = " hello"
println(s.trimAndCapitalize()) // Hello

Extension-функции для коллекций

fun List<Int>.sumOfSquares(): Int {
return this.map { it \* it }.sum()
}
val numbers = listOf(1, 2, 3)
println(numbers.sumOfSquares()) // 14

Extension-функции в классе

Можно объявлять расширения внутри классов:

class Logger {
fun String.log() {
println("LOG: $this")
}
fun doSomething() {
"Hello".log()
}
}

Такие расширения видны только внутри Logger.

Extension-функции vs обычные методы

  • Extension-функции не могут переопределить методы класса;

  • Если у класса уже есть метод с таким именем — будет вызван именно оригинальный метод, а не extension;

  • Extension-функции не имеют доступа к private и protected членам расширяемого класса.

Использование с Generics

fun <T> List<T>.secondOrNull(): T? {
return if (this.size > 1) this\[1\] else null
}
val list = listOf("a", "b", "c")
println(list.secondOrNull()) // b

Extension-поля?

В Kotlin нельзя добавить новое поле через extension. Только методы (или свойства, реализованные через геттер).

val String.firstChar: Char
get() = this\[0\]
val c = "Scala".firstChar // 'S'

Часто используемые расширения

Kotlin стандартная библиотека содержит множество extension-функций:

  • "Hello".length

  • "text".substring(1..3)

  • listOf(1,2,3).joinToString(", ")

  • mapOf("a" to 1).keys

В Android

Расширения активно используются:

fun Context.toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
context.toast("Привет!")

Таким образом, extension-функции в Kotlin — это мощный инструмент для расширения функциональности типов без модификации их исходного кода. Они позволяют писать лаконичный, читаемый и выразительный код, следуя принципам функционального и объектно-ориентированного стилей.