Наследование и интерфейсы
Учимся выстраивать иерархии типов через наследование и интерфейсы.
Наследование — механизм, позволяющий одному классу перенять свойства и поведение другого и при необходимости переопределить их.
Наследование помогает переиспользовать код и описывать отношения «является частным случаем». В Kotlin классы по умолчанию закрыты для наследования — это сознательное решение в пользу надёжности.
open и override
Чтобы от класса можно было наследоваться, его помечают open; то же касается методов, которые разрешено переопределять. Переопределение явно отмечают словом override.
open class Animal(val name: String) {
open fun sound() = "..."
}
class Dog(name: String) : Animal(name) {
override fun sound() = "Гав"
}
fun main() {
val d = Dog("Рекс")
println("${d.name}: ${d.sound()}")
}Вывод:
Рекс: Гав
Интерфейсы
Интерфейс описывает набор методов, которые класс обязуется реализовать. В отличие от класса, реализовать можно сразу несколько интерфейсов.
interface Drawable {
fun draw(): String
}
interface Clickable {
fun click() = "Клик!" // метод по умолчанию
}
class Button : Drawable, Clickable {
override fun draw() = "[Кнопка]"
}
fun main() {
val b = Button()
println(b.draw())
println(b.click())
}Вывод:
[Кнопка] Клик!
Абстрактные классы
abstract-класс нельзя создать напрямую — он служит основой для наследников и может содержать как абстрактные методы (без тела), так и обычные.
abstract class Shape {
abstract fun area(): Double
fun describe() = "Площадь: ${area()}"
}
class Circle(val r: Double) : Shape() {
override fun area() = 3.14 * r * r
}
fun main() {
println(Circle(2.0).describe())
}Вывод:
Площадь: 12.56
Как работает под капотом
В Java классы и методы открыты для наследования по умолчанию, и это породило много хрупкого кода. Kotlin поступил наоборот: всё final (закрыто), пока вы явно не разрешили наследование словом open. Это заставляет автора класса осознанно решать, что можно переопределять. Интерфейсы же могут содержать реализацию методов по умолчанию, но не могут хранить состояние (поля с значением).
Частые ошибки
- Забыть
openу класса или метода. Без него наследоваться/переопределять нельзя — будет ошибка компиляции. - Путать класс и интерфейс. Класс наследуют один, интерфейсов реализуют сколько угодно.
- Пытаться создать
abstract-класс напрямую. Нужно создавать его наследника.
Итог
- Классы и методы по умолчанию закрыты; для наследования нужен
open. - Переопределение отмечают словом
override. - Класс наследуется от одного класса, но реализует много интерфейсов.
abstract-класс нельзя создать напрямую — только через наследника.