Extension function у Kotlin: що це, синтаксис і застосування

Автор: IT Sectr Опубліковано: 2026-06-21 Час читання: 9 хв

extension function — механізм Kotlin, що дозволяє додавати нові методи до існуючих класів без успадкування та без зміни вихідного коду. Функція оголошується з префіксом у вигляді типу-отримувача та викликається як звичайний метод цього типу. За даними Kotlin Documentation, 2025, extension functions компілюються в статичні методи з першим параметром-отримувачем, що гарантує нульовий overhead у рантаймі порівняно зі звичайними викликами.

Головне

  • Extension function — метод із типом-отримувачем, який викликається як член класу без успадкування
  • Receiver type вказується через крапку перед іменем функції: fun Type.method()
  • Статична диспетчеризація — extension function не віртуальна, викликається за типом змінної
  • Імпорт обов'язковий: extension functions не є членами класу та потребують імпорту
  • Члени класу мають пріоритет: якщо метод збігається — викликається член класу, а не extension

Що таке extension function у Kotlin?

Extension function — це функція, оголошена поза класом, але викликана як його метод. Вона приймає тип-отримувач, до якого додається новий метод. Усередині функції ключове слово this посилається на екземпляр receiver-типу. Такий підхід дозволяє розширювати фінальні класи, класи сторонніх бібліотек і навіть примітивні типи без створення обгорток.

На відміну від Java, де для додавання методу до існуючого класу потрібне або успадкування, або статичний утилітарний клас, Kotlin пропонує елегантне рішення без boilerplate. За даними дослідження Kotlin Foundation (2024), extension functions входять до п'ятірки найбільш використовуваних можливостей мови — їх застосовують 78% Kotlin-розробників у повсякденній роботі.

Компілятор Kotlin транслює extension function у статичний метод, де першим аргументом передається екземпляр receiver. Це означає, що extension functions не змінюють байт-код цільового класу та не порушують інкапсуляцію — доступні лише публічні поля та методи receiver.

Використовуйте extension functions для написання чистих утилітарних функцій, які логічно прив'язані до певного типу, але не можуть бути додані до його вихідного коду.

Синтаксис extension function: оголошення та виклик

Базовий синтаксис extension function: ім'я типу-отримувача, крапка, ім'я функції, потім параметри та тип, що повертається. Усередині функції receiver доступний через this.

kotlin
// Extension function for String
fun String.isEmail(): Boolean {
    return this.contains("@") && this.contains(".")
}

// Called as regular String method
val result = "user@example.com".isEmail() // true

У цьому прикладі isEmail() стає доступним для всіх рядків. Усередині функції this — це сам рядок, для якого викликається метод. Kotlin дозволяє опускати this у більшості випадків — можна писати просто contains("@") замість this.contains("@").

Extension function з параметрами

Extension functions можуть приймати додаткові параметри та повертати значення будь-якого типу. Це робить їх повноцінними функціями, а не просто синтаксичним цукром.

kotlin
fun List<Int>.defaultIfEmpty(default: Int): List<Int> {
    if (this.isEmpty()) return listOf(default)
    return this
}

val populated = listOf(1, 2).defaultIfEmpty(0)
val empty = listOf<Int>().defaultIfEmpty(0)

Параметр default визначає значення, яке повертається для порожнього списку. Extension function може бути generic — тип List узагальнений, хоча в прикладі використовується конкретний List.

Статична диспетчеризація та receiver

Extension functions у Kotlin використовують статичну диспетчеризацію, а не віртуальну. Це ключова відмінність від звичайних методів класів. Яка extension function буде викликана, визначається на етапі компіляції за статичним типом змінної, а не за її фактичним типом у рантаймі.

kotlin
open class Animal
class Dog : Animal()

fun Animal.speak() = "Animal sound"
fun Dog.speak() = "Woof"

fun test() {
    val animal: Animal = Dog()
    println(animal.speak()) // "Animal sound" — static type Animal

    val dog: Dog = Dog()
    println(dog.speak()) // "Woof" — static type Dog
}

Незважаючи на те, що змінна animal вказує на екземпляр Dog, викликається extension function для Animal, тому що статичний тип змінної — Animal. Якби speak() був віртуальним методом класу, викликався б Dog.speak(). Цю властивість важливо враховувати при проектуванні API на extension functions.

Імпорт та область видимості extension functions

Extension functions не є членами класу — це звичайні функції, які потребують імпорту для використання в іншому файлі. Kotlin надає два варіанти імпорту: за іменем функції або з перейменуванням.

kotlin
// Import by name
import com.example.extensions.isEmail

// Import with alias (to resolve conflicts)
import com.example.extensions.isEmail as isValidEmail

// Call after import
val valid = "test@test.com".isEmail()

Перейменування через as корисне при конфліктах імен — наприклад, якщо дві бібліотеки надають extension function з однаковою назвою для одного типу. У цьому випадку можна імпортувати одну з них з іншим іменем і викликати через нове ім'я.

Область видимості

Extension functions можна оголошувати на різних рівнях:

РівеньВидимістьПриклад
Top-levelВесь проект після імпортуfun String.isEmail()
Member extensionУсередині класу-власникаclass A { fun B.ext() }
LocalУсередині функціїfun test() { fun String.ext() }

Member extension functions — особливий випадок, коли extension function оголошується всередині іншого класу. У цьому випадку всередині extension function доступні як receiver (this функції), так і члени зовнішнього класу.

Пріоритет членів класу над розширеннями

Якщо у класу є метод з тією ж сигнатурою, що й extension function, завжди викликається член класу. Extension function ніколи не перевизначає метод класу — це архітектурне рішення для запобігання випадковим перевизначенням.

kotlin
class User {
    fun greet() = "Hello from class"
}

fun User.greet() = "Hello from extension"

fun main() {
    val user = User()
    println(user.greet()) // "Hello from class"
}

Навіть якщо extension function визначена пізніше і має ту саму сигнатуру, компілятор вибере метод класу. Компілятор Kotlin виводить попередження при виявленні такої ситуації. Єдиний спосіб викликати extension function при наявності методу класу — звернутися до неї як до звичайної функції: greet(user).

Практичні приклади extension functions

У Android-розробці extension functions стали стандартним інструментом для роботи з View, Context і фрагментами. Бібліотека Android KTX побудована на extension functions, надаючи зручні обгортки над API Android.

kotlin
// Extension function for working with View
fun View.show() {
    visibility = View.VISIBLE
}

fun View.hide() {
    visibility = View.GONE
}

// Extension function for Context — toast
fun Context.toast(message: String) {
    Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

// Extension function as DSL builder
fun String.colorFormat(color: String): String =
    "$color$this\u001b[0m"

Extension function toast() робить Context-залежні виклики лаконічними: замість написання Toast.makeText(context, message, length).show() достатньо написати context.toast("Текст"). Це зменшує boilerplate і робить код більш читабельним.

Часті запитання

Чи можна перевизначити extension function у підкласі?

Ні, extension functions використовують статичну диспетчеризацію. Якщо оголосити extension function для базового класу і таку ж для підкласу, яка з них викличеться — визначається статичним типом змінної на етапі компіляції, а не фактичним типом у рантаймі.

Як extension functions працюють з nullable-типами?

Можна оголошувати extension functions для nullable receiver-типів: fun String?.isNullOrEmail(). Усередині такої функції this може бути null, тому необхідно використовувати оператори безпечного виклику або явну перевірку на null.

Чи є у extension functions overhead у продуктивності?

Ні, extension functions компілюються в статичні методи. На рівні байт-коду виклик extension function ідентичний виклику статичного методу з першим параметром-отримувачем. Жодного рефлексіону або динамічної диспетчеризації.

Чи можна оголосити extension property для класу?

Так, extension properties працюють аналогічно extension functions, але не можуть зберігати стан — тільки геттер і сеттер. Наприклад: val List.sum get() = reduce(Int::plus). Вони також компілюються в статичні методи getter/setter.

Чим extension functions відрізняються від методів у Java утилітарних класах?

У Java ви пишете StringUtils.isEmail(str), а в Kotlin — str.isEmail(). Різниця не лише в синтаксисі: extension functions підтримують автодоповнення в IDE, покращують читабельність ланцюжків викликів і дозволяють IDE пропонувати релевантні функції для конкретного типу. У Java таких можливостей немає.

Підсумки

  • Extension function — метод із типом-отримувачем, який викликається як член класу без успадкування
  • Статична диспетчеризація — вибір функції за статичним типом, а не за рантайм-типом
  • Нульовий overhead — компілюється в статичний метод із receiver як першим параметром
  • Імпорт обов'язковий для використання в інших файлах, можливий alias через as
  • Члени класу мають пріоритет над extension functions з тією ж сигнатурою
  • Member extensions — розширення, оголошені всередині класу, мають доступ до його членів
  • Практичне застосування — утиліти для String, View, Context, колекцій і DSL-побудова

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також