Прямые ссылки на публичные уроки для быстрого старта и стабильной индексации lesson-страниц.
Extension-функция — это функция, которая не находится внутри класса, но расширяет его функционал, имея доступ к его публичным полям.
Рассмотрим на примере класса File из стандартной библиотеки Java. Создадим экземпляр этого класса:
fun main() { val file = File("textFile.txt") }
File — это класс, конструктор которого принимает путь к файлу. Внутри него есть поля и методы для работы с файлами, например createNewFile().
Предположим, нам нужен метод, который записывает текст в файл и одновременно выводит его в консоль. В классе File такого метода нет, а редактировать его исходный код нельзя — он находится внутри JDK в скомпилированном виде и доступен только для чтения.
Создадим extension-функцию для класса File. Extension-функция объявляется как обычная с ключевого слова fun, но перед названием указывается тип класса, для которого расширяется функционал.
fun main() { val file = File("textFile.txt") file.writeToFileAndPrint() } fun File.writeToFileAndSend() { val message = "some message for file" writeText(message) println("Запись \"$message\" добавлена в файл $name и отправлена на сервер") }
Внутри extension-функции можно вызывать методы и обращаться к свойствам расширяемого класса. В примере writeText() и name — это члены класса File. Первая записывает текст в файл, вторая является сокращённой формой геттера getName().
Стандартная библиотека Kotlin сама построена на этом механизме: функция writeText() — extension-функция класса File, встроенная в API Kotlin. Её сигнатура идентична той, что мы написали сами. При этом функция не является членом класса File — она объявлена отдельно, но расширяет его функционал за счёт указания типа перед названием.
Без Kotlin такой функционал пришлось бы реализовывать вручную через FileOutputStream, запись массивов байтов и т.д. Extension-функции API Kotlin решают это одной строкой.
Помимо функций существуют extension-свойства. Важно понимать: расширения не добавляют реальных членов к классу, поэтому свойство-расширение не может иметь backing field (field). По той же причине для них нельзя использовать инициализаторы — поведение определяется только явными геттерами и сеттерами.
Пример: объявим свойство-расширение nameWithoutExtension для получения имени файла без расширения.
public val File.nameWithoutExtension: String get() = name.substringBeforeLast(".")
val File.nameWithoutExtension: String get() = name.substringBeforeLast(".") fun main() { val file = File("textFile.txt") println(file.nameWithoutExtension) }
Extension-функции позволяют расширять классы, к исходному коду которых нет доступа. Однако не стоит ими злоупотреблять: если класс внутри проекта требует расширения, лучше добавить метод прямо в него. Создание большого количества extension-функций в разных местах может размазать логику по проекту и затруднить её поддержку.