Функциональные (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–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/fun-interfaces.html