Функциональные (SAM) интерфейсы
Интерфейс, содержащий только один абстрактный метод, называется функциональным интерфейсом или интерфейсом с одним абстрактным методом (SAM). Функциональный интерфейс может иметь несколько неабстрактных членов, но только один абстрактный член.
Для объявления функционального интерфейса в Kotlin используйте модификатор fun.
fun interface KRunnable {
fun invoke()
}
Преобразования SAM
Для функциональных интерфейсов вы можете использовать преобразования SAM, которые помогают сделать ваш код более лаконичным и читабельным, используя лямбда-выражения.
Вместо создания класса, который реализует функциональный интерфейс вручную, вы можете использовать лямбда-выражение. С помощью преобразования SAM Kotlin может преобразовать любое лямбда-выражение, чья сигнатура соответствует сигнатуре единственного метода интерфейса, в код, который динамически создаёт реализацию интерфейса.
Например, рассмотрим следующий функциональный интерфейс Kotlin:
fun interface IntPredicate {
fun accept(i: Int): Boolean
}
Если вы не используете преобразование SAM, вам потребуется написать код подобный этому:
// Creating an instance of a class
val isEven = object : IntPredicate {
override fun accept(i: Int): Boolean {
return i % 2 == 0
}
}
Используя преобразование SAM Kotlin, вы можете написать следующий эквивалентный код вместо этого:
// Creating an instance using lambda
val isEven = IntPredicate { it % 2 == 0 }
Короткий лямбда-выражение заменяет весь ненужный код.
fun interface IntPredicate {
fun accept(i: Int): Boolean
}
val isEven = IntPredicate { it % 2 == 0 }
fun main() {
println("Is 7 even? - ${isEven.accept(7)}")
}
Вы также можете использовать преобразования SAM для Java-интерфейсов.
Миграция от интерфейса с функцией-конструктором к функциональному интерфейсу
Начиная с версии 1.6.20, Kotlin поддерживает ссылки на вызываемые объекты для конструкторов функциональных интерфейсов, что добавляет совместимый со старым кодом способ миграции от интерфейса с функцией-конструктором к функциональному интерфейсу. Рассмотрим следующий код:
interface Printer {
fun print()
}
fun Printer(block: () -> Unit): Printer = object : Printer { override fun print() = block() }
С поддержкой ссылок на вызываемые объекты для конструкторов функциональных интерфейсов этот код может быть заменён лишь объявлением функционального интерфейса:
fun interface Printer {
fun print()
}
Его конструктор будет создан неявно, и любой код, использующий ссылку на функцию ::Printer, будет компилироваться. Например:
documentsStorage.addPrinter(::Printer)
Сохраните двоичную совместимость, пометив устаревшую функцию Printer аннотацией @Deprecated со значением DeprecationLevel.HIDDEN.
@Deprecated(message = "Your message about the deprecation", level = DeprecationLevel.HIDDEN)
fun Printer(...) {...}
Функциональные интерфейсы против псевдонимов типов
Вы также можете просто переписать вышесказанное, используя псевдоним типа для функционального типа:
typealias IntPredicate = (i: Int) -> Boolean
val isEven: IntPredicate = { it % 2 == 0 }
fun main() {
println("Is 7 even? - ${isEven(7)}")
}
Однако функциональные интерфейсы и псевдонимы типов служат разным целям. Псевдонимы типов - это просто имена для существующих типов; они не создают новый тип, в то время как функциональные интерфейсы это делают. Вы можете предоставить расширения, специфичные для определенного функционального интерфейса, чтобы они не применялись к обычным функциям или их псевдонимам типов.
Псевдонимы типов могут иметь только один член, в то время как функциональные интерфейсы могут иметь несколько неабстрактных членов и один абстрактный член. Функциональные интерфейсы также могут реализовывать и расширять другие интерфейсы.
Функциональные интерфейсы более гибкие и предоставляют больше возможностей, чем псевдонимы типов, но они могут быть более затратными как синтаксически, так и во время выполнения, поскольку могут потребоваться преобразования в конкретный интерфейс. При выборе того, какой из них использовать в вашем коде, учитывайте свои потребности:
Если ваш API должен принимать функцию (любую функцию) с определёнными параметрами и типами возвращаемых значений - используйте простой функциональный тип или определите псевдоним типа, чтобы дать более короткое имя соответствующему функциональному типу.
Если ваш API принимает более сложную сущность, чем функция – например, она имеет нетривиальные контракты и/или операции над ней, которые невозможно выразить в сигнатуре функционального типа – объявите отдельный функциональный интерфейс для неё.
© 2010–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/fun-interfaces.html