Интерфейсы
Интерфейсы в Kotlin могут содержать объявления абстрактных методов, а также методы с реализацией. Главное отличие интерфейсов от абстрактных классов заключается в невозможности хранения переменных экземпляров. Они могут иметь свойства, но те должны быть либо абстрактными, либо предоставлять реализацию методов доступа.
Интерфейс определяется ключевым словом interface
.
interface MyInterface {
fun bar()
fun foo() {
// необязательное тело
}
}
Реализация интерфейсов
Класс или объект могут реализовать любое количество интерфейсов.
class Child : MyInterface {
override fun bar() {
// тело
}
}
Свойства в интерфейсах
Вы можете объявлять свойства в интерфейсах. Свойство, объявленное в интерфейсе, может быть либо абстрактным, либо иметь свою реализацию методов доступа. Свойства в интерфейсах не могут иметь теневых полей, соответственно, методы доступа к таким свойствам не могут обращаться к теневым полям.
interface MyInterface {
val prop: Int // абстрактное свойство
val propertyWithImplementation: String
get() = "foo"
fun foo() {
print(prop)
}
}
class Child : MyInterface {
override val prop: Int = 29
}
Наследование интерфейсов
Интерфейс может быть производным от других интерфейсов, что означает, что он может как предоставлять реализации для их членов, так и объявлять новые функции и свойства. Вполне естественно, что классы, реализующие такой интерфейс, требуются только для определения отсутствующих реализаций.
interface Named {
val name: String
}
interface Person : Named {
val firstName: String
val lastName: String
override val name: String get() = "$firstName $lastName"
}
data class Employee(
// реализация 'name' не требуется
override val firstName: String,
override val lastName: String,
val position: Position
) : Person
Устранение противоречий при переопределении
Когда мы объявляем большое количество типов в списке нашего супертипа, может так выйти, что мы допустим более одной реализации одного и того же метода.
interface A {
fun foo() { print("A") }
fun bar()
}
interface B {
fun foo() { print("B") }
fun bar() { print("bar") }
}
class C : A {
override fun bar() { print("bar") }
}
class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
override fun bar() {
super<B>.bar()
}
}
Оба интерфейса A и B объявляют функции foo()
и bar()
. Оба реализуют foo()
, но только B содержит реализацию bar()
(bar()
не отмечен как абстрактный метод в интерфейсе A, потому что в интерфейсах это подразумевается по умолчанию, если у функции нет тела).
Теперь, если вы унаследуете какой-нибудь класс C от интерфейса A, вам, очевидно, придётся переопределять метод bar()
, обеспечивая его реализацию.
Однако если вы унаследуете класс D от интерфейсов A и B, вам надо будет переопределять все методы, унаследованные от этих интерфейсов,
и вам нужно указать, как именно D должен их реализовать.
Это правило касается как тех методов, у которых имеется только одна реализация (bar()
), так и тех, у которых есть несколько реализаций (foo()
).