Урок 21: Extension (функции-расширения) языка Kotlin

Extensions функции расширения экстеншены - Android [Kotlin] для начинающих

extension функции

Extension функция – это функция, которая не являясь участником какого-то класса (то есть не находясь внутри определенного класса), расширяет его функционал, имея доступ к публичным полям.

Чтобы лучше понять что это такое, лучше разобраться в вопросе “когда применяется extension функция?”

Когда применяется extension функция?

Разберем на примере класса File из стандартной библиотеки Java. Создадим экземпляр этого класса, в скобках указываем название будущего файла.

fun main() {
	val file = File("textFile.txt")
}

Провалимся в документацию. File – это класс. Конструктор принимает путь к файлу в систему — в нашем случае относительный путь. У него внутри есть поля и методы для работы с файлами. Например, метод createNewFile() — создает файл, если его не существует.

Но предположим, что нам нужен такой метод для записи текста в файл, который еще и отображает в консоль то, что записано в файл. В классе File этого метода нет, а также у вас нет доступа для редактирования этого файла. Он по умолчанию read only. Потому, что этот файл находится внутри JDK. Если нажать на “прицел”, увидим его расположение. Вот тут находятся все инструменты, что мы выбирали при создании проекта. И здесь в стандартной библиотеке лежит этот файл класса File в скомпилированном виде. Редактировать его нельзя.

Применение функции — расширения

Нам нужно записать текст в файл и выполнить какие-то дополнительные манипуляции. Поэтому создадим функцию-расширение для класса File, которая будет записывать текст в файл и выводить информацию в консоль. Дословно это будет звучать так: хочу, чтобы у файла (у класса 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 и отправлена на сервер")
}

И, вызвав эту функцию у объекта класса File, к нему применится определенное действие. Причем можно не просто добавлять свой функционал, а можно еще внутри вызывать какие-то функции из класса, к которому применяем функцию-расширения. В данном случае createNewFile() и getName() (здесь она написана в сокращенном формате name, вместо функции геттера) это функции класса File. *writeText*() тоже применяется к объекту файла, но не напрямую. Об этом будет ниже.

Разработчики стандартной библиотеки Kotlin уже встроили функцию writeText() в стандартную библиотеку Kotlin. Провалимся в исходник и увидим такую же сигнатуру, которую мы написали для создания собственной extension функции. В Котлин уже есть собственная функция для записи текста в файл и она является частью языка. Как видите, функция НЕ является частью класса File. Она вообще не рядом с классом File. Однако, расширяет его функционал, за счет указания типа перед названием в сигнатуре функции.

То есть если бы мы писали этот функционал без Котлин, нужно было бы самостоятельно реализовывать весь функционал, описанный в этих функциях. Создавать FileOutputStream, записывать массивы байтов и так далее.

Но сейчас, например, чтобы записать текст в файл нам нужно только воспользоваться extension функцией, которую предоставляет API Kotlin. И применить ее к экземпляру класса File. Такие функции в среде разработки подсвечиваются желтым цветом.

extension-свойства

Помимо функций есть также extension-свойства. Расширения на самом деле не добавляют никаких членов к классам и свойство-расширение не может иметь контейнеры field. Еще их называют Backing fields. Для свойств-расширений нельзя использовать инициализаторы. Их поведение может быть определено только явным образом, с указанием геттеров/сеттеров.

Приведем такой пример. Объявим свойство-расширение 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)
}

Вот в чем крутость Kotlin и крутость функций-расширения. Extension функции позволяют расширять функционал классов, к исходному коду которого у вас нет доступа. Но добавлю на будущее. Не стоит злоупотреблять такими функциями. Если внутри проекта есть какой-то класс, который требует расширения – просто добавьте в него требуемый метод. Во избежание ненужного разброса, так как ваш написанный класс будет в одном месте. А extension’ов вы насоздаете в другом, что может размазать логику по проекту.

Взаимодействие с файлами и extension’ами мы используем в рамках создания курсового проекта телеграм-бота на Kotlin. А также участникам практики доступны более продвинутые темы.

Для тех, кто собрался стать Android-разработчиком

Пошаговаясхема
Пошаговая
схема

Описание процесса обучения от основ Kotlin до Android-разработчика

Бесплатныеуроки
Бесплатные
уроки

Авторский бесплатный курс по основам языка программирования Kotlin

Обучающийбот
Обучающий
бот

Тренажер и самоучитель по Котлин – бесплатные тесты и практика

Поделиться уроком

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *