| type | doc |
|---|---|
| layout | reference |
| category | Syntax |
| title | Интерфейсы |
| url | https://kotlinlang.ru/docs/reference/interfaces.html |
Интерфейсы в Kotlin очень похожи на интерфейсы в Java 8. Они могут содержать абстрактные методы, методы с реализацией. Главное отличие интерфейсов от абстрактных классов заключается в невозможности хранения переменных экземпляров. Они могут иметь свойства, но те должны быть либо абстрактными, либо предоставлять реализацию методов доступа.
Интерфейс определяется ключевым словом interface:
interface MyInterface {
fun bar()
fun foo() {
// необязательное тело
}
}Класс или объект могут реализовать любое количество интерфейсов:
class Child : MyInterface {
override fun bar() {
// тело
}
}Вы можете объявлять свойства в интерфейсах. Свойство, объявленное в интерфейсе, может быть либо абстрактным, либо иметь свою реализацию методов доступа. Свойства в интерфейсах не могут иметь backing fields, соответственно, методы доступа к таким свойствам не могут обращаться к backing fields.
interface MyInterface {
val prop: Int // абстрактное свойство
val propertyWithImplementation: String
get() = "foo"
fun foo() {
print(prop)
}
}
class Child : MyInterface {
override val prop: Int = 29
}Когда мы объявлем большое количество типов в списке нашего супертипа, может так выйти, что мы допустим более одной реализации одного и того же метода. Например:
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()
}
}Оба интерфейса A и B объявляют функции foo() и bar(). Оба реализуют foo(), но только B содержит реализацию bar() (bar() не отмечен как абстрактный метод в интерфейсе A, потому что в интерфейсах это подразумевается по умолчанию, если у функции нет тела). Теперь, если мы унаследуем какой-нибудь класс C от A, нам, очевидно, придётся переопределять bar(), обеспечивать его реализацию. А если мы унаследуем D от A и B, нам не надо будет переопределять bar(), потому что мы унаследовали только одну его имплементацию. Но мы получили в наследство две имплементации foo(), поэтому компилятору не известно, какую выбрать. Он заставит нас переопределить функцию foo() и явно указать что мы имели ввиду.