Menu
Coddy logo textTech

Объявление интерфейсов

Часть раздела Объектно-ориентированное программирование путешествия по Kotlin на Coddy. Урок 22 из 57.

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

interface Drawable {
    fun draw(): String
}
class Circle(val radius: Int) : Drawable {
    override fun draw() = "circle with radius $radius"
}
class Line(val length: Int) : Drawable {
    override fun draw() = "-".repeat(length)
}

Внутри main:

println(Circle(3).draw())
println(Line(5).draw())

Вывод:

circle with radius 3
-----

Интерфейс также может объявлять свойства. Класс предоставляет их как свойства конструктора, хранимые свойства или геттеры; сам интерфейс ничего не хранит:

interface Vehicle {
    val wheels: Int
    val label: String
}
class Car(override val label: String) : Vehicle {
    override val wheels = 4
}
class Bike : Vehicle {
    override val wheels = 2
    override val label: String
        get() = "bike on $wheels wheels"
}

Внутри main:

println("${Car("taxi").label}: ${Car("taxi").wheels}")
println(Bike().label)

Результат:

taxi: 4
bike on 2 wheels

Каждый член, объявленный интерфейсом, должен быть реализован, причём для каждого из них необходимо указать override, иначе класс не скомпилируется. Экземпляр интерфейса создать нельзя:

interface Speaker {
    fun speak(): String
    val volume: Int
}
// class Mute : Speaker { override fun speak() = "" }   // ошибка: volume не реализован
// class Loud : Speaker() { ... }                      // ошибка: у интерфейса нет конструктора
// val s = Speaker()                                   // ошибка: нельзя создать интерфейс

Интерфейс и абстрактный класс объявляют члены без тела. У интерфейса нет конструктора и сохранённого состояния, а класс может реализовывать множество интерфейсов, но расширять только один класс. Используй интерфейс для возможности, общей для несвязанных классов, например Drawable или Comparable, а абстрактный класс — для общего базового класса с состоянием.

interface Drawable { fun draw(): String }                   // возможность
abstract class Shape(val name: String) { abstract fun area(): Double }   // база с состоянием
class Square(val s: Double) : Shape("square"), Drawable {  // один класс, любые интерфейсы
    override fun area() = s * s
    override fun draw() = "[]"
}
challenge icon

Задание

Легко

Объяви члены интерфейса Shape: свойство только для чтения name и методы area() и perimeter(), оба возвращающие Double. Реализуй их в Rectangle(width, height) (имя rectangle) и Circle(radius) (имя circle, с использованием Math.PI). Затем дополни describe(shape), которая возвращает rectangle: area 12.00, perimeter 14.00 с двумя десятичными знаками, используя только члены Shape.

Предоставленный код считывает строки r,3,4 или c,2, создаёт соответствующую фигуру и выводит describe(shape).

Твой код должен находиться в Shape.kt, Rectangle.kt, Circle.kt и Functions.kt. В Main.kt находится предоставленный код ввода-вывода, и его нельзя редактировать.

Попробуйте сами

fun main() {
    // Предоставленный код ввода/вывода: оставьте его как есть
    val input = generateSequence(::readLine).toList()
    for (line in input) {
        val p = line.split(",").map { it.trim() }
        val shape: Shape = if (p[0] == "r") Rectangle(p[1].toDouble(), p[2].toDouble()) else Circle(p[1].toDouble())
        println(describe(shape))
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Песочница Kotlin