Extension functions/расширения — это механизм, позволяющий добавлять новые функции в уже существующие классы без необходимости наследования или изменения самого класса.
Например, для класса String, добавим функцию, переворачивающую строку:
fun String.reverse(): String { return this.reversed() } println("abc".reverse()) // "cba"
Расширения на самом деле не модифицируют класс, а просто являются синтаксическим сахаром: они компилируются как статические методы, принимающие экземпляр объекта как первый параметр.
Преимущества: лаконичность, читаемость, расширяемость кода. Хорошо ложатся на utility-функции для коллекций, строк и т.д.
Подводные камни:
Можно ли при помощи extension functions добавить новую переменную (property), хранящую состояние, к классу?
Ответ: Нет. Extension property — это всегда вычисляемое свойство (getter/setter), не поле. Они не могут хранить состояние — только вычислять на лету.
val String.secondChar: Char get() = this[1] // Только вычисление, не хранение!
История
В проекте по верификации данных разработчик добавил extension property к модельному классу как если бы оно хранило значение, но позже заметил, что значение всегда вычисляется, а не запоминается, из-за чего логика работала неверно при многократных вызовах.
История
В большом приложении расширения были названы так же, как методы класса, что привело к путанице: методы класса всегда имели приоритет, а расширения не вызывались — потрачен день на отладку "невидимого" кода.
История
В одной из библиотек расширения использовались для приватных полей класса, но позже оказалось, что они вообще не имеют прав доступа к private, из-за чего приходилось рефакторить архитектуру моделей.