Extension Functions

https://kotlinlang.org/docs/extensions.html
Extension Functions

Kotlin позволяет расширять класс или интерфейс новой функциональностью, не наследуясь от него и не используя шаблоны проектирования. Для того, чтобы объявить функцию-расширение, нужно указать в качестве префикса расширяемый тип. Расширения вычисляются статически. Расширения на самом деле не проводят никаких модификаций с классами, которые они расширяют. Объявляя расширение, вы создаёте новую функцию, а не новый член класса.

• Могут быть функциями или переменными.

• Под капотом - статическая функция Java, в качестве параметра - объект расширяемого типа.

val Int.toHexColor: String
    get() = String.format("#%06X", 0xFFFFFF and this)

fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
    val tmp = this[index1]
    this[index1] = this[index2]
    this[index2] = tmp
}
interface IAction {
    fun someA(): Int
    fun someB(): Int
}

fun IAction.sum(): Int {
    return someA() + someB()
}
Ограничения

• Не могут изменить поведение класса

Extension-функции не могут изменять внутреннее состояние класса или его поведение, они лишь добавляют новые методы. Все изменения доступны только на уровне использования функции и не влияют на сам класс.

// Не может изменить внутреннее состояние класса
fun String.isEmpty(): Boolean {
    return this.length == 0
}

• Не могут быть заменены (override)

Extension-функции не могут переопределять методы класса. Они действуют как статические методы, которые «расширяют» класс, но не изменяют его фактическую реализацию.

open class Base {
    open fun doSomething() {
        println("Base")
    }
}

fun Base.doSomething() {
    println("Extension")
}

// Вызовет метод класса, а не extension-функцию
val base = Base()
base.doSomething() // Выведет "Base"

• Не могут изменять существующие методы

Extension-функции не могут изменять существующие методы класса или изменять их видимость. Они могут только добавить новые методы или функции, но не могут изменить поведение уже существующих.

// Можно только добавить новый метод
fun String.customMethod() {
    println("Custom method")
}

// Не влияет на существующие методы String
"Hello".customMethod() // Выведет "Custom method"

• Не поддерживают полиморфизм

Extension-функции не поддерживают полиморфизм и не могут быть использованы для полиморфного вызова. Если в иерархии классов определены extension-функции, они не могут переопределяться в подклассах.

open class Animal
class Dog: Animal()

fun Animal.makeSound() {
    println("Animal sound")
}

fun Dog.makeSound() {
    println("Bark")
}

val animal: Animal = Dog()
animal.makeSound() // Выведет "Animal sound"

• Не могут быть вызваны через super

Extension-функции не могут быть вызваны через ключевое слово super, так как они не являются частью класса, а представляют собой статические методы.

open class Base {
    open fun print() {
        println("Base")
    }
}

fun Base.print() {
    println("Extension")
}

class Derived: Base() {
    override fun print() {
        super.print() // Вызовет метод Base, а не extension-функцию
    }
}

• Не поддерживают доступ к private-методам

Extension-функции не могут обращаться к private-свойствам класса, к которому они относятся. Они могут взаимодействовать только с public-членами класса.

class MyClass {
    private fun privateMethod() {}
}

fun MyClass.accessPrivate() {
    // Не может обратиться к privateMethod()
}
Вопросы на собесе (9)
  1. Что такое extension-функции в Kotlin и зачем они нужны?

    Позволяют добавлять новые функции к существующим классам без необходимости их наследования или изменения. Они нужны для улучшения читаемости кода и повышения его удобства, позволяя вызывать функции так, как если бы они были частью оригинального класса, что упрощает работу с API и библиотеками.

  1. Как extension-функции выглядят в Java? (работают под капотом)

    Становится вызовом статического метода с передачей объекта в качестве первого параметра.

  1. Какие ограничения есть у extension-функций?

    Не могут изменить поведение класса.

    Не могут быть заменены (override).

    Не могут изменять существующие методы.

    Не поддерживают полиморфизм.

    Не могут быть вызваны через super.

    Не поддерживают доступ к private-методам.

  1. Что будет если сигнатура extension-функции совпадет с внутренней функцией класса?

    Вызовется внутренняя функция, компилятор подсветит что имя занято.

  1. В каких случаях extension-функция не развернется в статическую функцию Java?

    Если она объявлена внутри класса или внутри другой extension-функции.

  1. Можно ли обращаться к приватным полям класса из extension-функции?

    Можно только если extension-функция объявлена внутри класса.

  1. Почему нельзя все функции класса сделать extension?

    Потому что extension-функции не могут получить доступ к приватным свойствам и методам класса. Кроме того, они не могут переопределять методы и не поддерживают полиморфизм, что ограничивает их функциональность в сравнении с методами класса.

  1. Какой результат выведет функция main?
    fun main() {
    		val repository = UserRepository()
    		println(repository.getName())
    }
    
    class UserRepository {
    		fun getName(id: Int = 4): String {
    				return "Иван"
    		}
    }
    
    fun UserRepository.getName(): String {
    		return "Николай"
    }

    Правильный ответ: Иван.

  1. Напиши свою extension-функцию filter?
    fun <T> List<T>.myFilter(predicate: (T) -> Boolean): List<T> {
        val result = mutableListOf<T>()
        for (item in this) {
            if (predicate(item)) {
                result.add(item)
            }
        }
        return result
    }