Функциональные (SAM) интерфейсы

Интерфейсы только с одним абстрактным методом называются функциональными интерфейсами или Single Abstract Method (SAM) интерфейсами. Функциональный интерфейс может иметь несколько неабстрактных членов, но только один абстрактный.

Чтобы объявить функциональный интерфейс, используйте модификатор fun.

fun interface KRunnable {
   fun invoke()
}

SAM преобразования

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

Вместо создания класса, который реализует функциональный интерфейс вручную, вы можете использовать лямбда-выражения. Вместе с SAM преобразованиями Kotlin может преобразовывать лямбда-выражения, сигнатура которых совпадает с сигнатурой единственного метода интерфейса, в код, динамически создающий экземпляр реализации интерфейса.

Например, рассмотрим следующий функциональный интерфейс Kotlin:

fun interface IntPredicate {
   fun accept(i: Int): Boolean
}

Если вы не используете SAM преобразования, вам будет нужно написать следующий код:

// Создание экземпляра класса
val isEven = object : IntPredicate {
   override fun accept(i: Int): Boolean {
       return i % 2 == 0
   }
}

Используя SAM преобразование, вы можете использовать следующий эквивалентный код:

// Создание экземпляра, используя лямбду
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 интерфейсов.

Функциональные интерфейсы vs псевдонимы типов

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

Псевдонимы типов могут иметь только один элемент, в то время как функциональные интерфейсы могут иметь несколько неабстрактных элементов и один абстрактный элемент. Функциональные интерфейсы также могут реализовывать и расширять другие интерфейсы.

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

  • Если вашему API необходимо принять функцию (любую функцию) с определенным параметром и типами возвращаемых значений –используйте простой функциональный тип или определите псевдоним типа, чтобы дать более короткое имя соответствующему функциональному типу.
  • Если ваш API принимает более сложную сущность, чем функция, – например, у него есть нетривиальные контракты и/или операции над ним, которые не могут быть выражены в сигнатуре функционального типа, объявите для него отдельный функциональный интерфейс.