Абстрактные классы
Часть раздела Объектно-ориентированное программирование путешествия по Kotlin на Coddy. Урок 20 из 57.
Абстрактный класс — это суперкласс, который нельзя создать самостоятельно. Он может объявлять абстрактные члены без тела, и каждый конкретный подкласс должен их реализовать:
abstract class Shape(val name: String) {
abstract fun area(): Double
fun describe() = "$name with area ${area()}"
}
class Circle(val r: Double) : Shape("circle") {
override fun area() = 3.14 * r * r
}
class Rect(val w: Double, val h: Double) : Shape("rect") {
override fun area() = w * h
}Внутри main:
val shapes: List<Shape> = listOf(Circle(1.0), Rect(2.0, 3.0))
for (s in shapes) println(s.describe())Результат:
circle with area 3.14
rect with area 6.0Абстрактный класс автоматически имеет модификатор open, и его абстрактные члены тоже имеют модификатор open, поэтому в подклассах нужно писать только override. Создание самого абстрактного класса не компилируется, а подкласс, в котором пропущен абстрактный член, также должен быть абстрактным. Используй абстрактный класс, когда у суперкласса нет осмысленного собственного поведения для некоторых членов:
// val s = Shape("blob") // ошибка: нельзя создать экземпляр абстрактного класса
// class Blob : Shape("blob") // ошибка: Blob не реализует area()
abstract class Polygon(name: String) : Shape(name) // нормально: всё ещё абстрактныйАбстрактный класс может иметь конструктор, состояние и обычные методы, которые наследуют подклассы. Распространённый вариант использования — фиксированная процедура, шаги которой могут различаться, — шаблонный метод: метод суперкласса выполняет шаги, а подклассы реализуют абстрактные из них:
abstract class Report(val title: String) {
fun render() = "== $title ==\n${body()}\n(${footer()})"
abstract fun body(): String
open fun footer() = "end"
}
class Sales(val total: Int) : Report("Sales") {
override fun body() = "total: $total"
}
class Stock(val items: Int) : Report("Stock") {
override fun body() = "items: $items"
override fun footer() = "counted today"
}Внутри main:
println(Sales(420).render())
println(Stock(17).render())Вывод:
== Sales ==
total: 420
(end)
== Stock ==
items: 17
(counted today)Абстрактные свойства работают таким же образом. Подкласс предоставляет значение в качестве свойства конструктора, хранимого свойства или геттера:
abstract class Worker(val name: String) {
abstract val monthly: Int
fun yearly() = monthly * 12
}
class Salaried(name: String, override val monthly: Int) : Worker(name)
class Hourly(name: String, val hours: Int, val rate: Int) : Worker(name) {
override val monthly: Int
get() = hours * rate
}Внутри main:
val staff = listOf(Salaried("Ada", 4000), Hourly("Bo", 80, 25))
for (w in staff) println("${w.name}: ${w.yearly()}")Результат:
Ada: 48000
Bo: 24000Задание
ЛегкоDiscount — это абстрактный класс. Он объявляет абстрактный метод amount(price) и реализует apply(price), который возвращает цену за вычетом amount(price), но никогда не возвращает значение меньше 0. Завершите реализацию подклассов: PercentOff(percent) вычитает из цены этот процент (с округлением вниз), FixedOff(value) вычитает фиксированное значение, а SpendMore(threshold, off) вычитает off только тогда, когда цена не меньше threshold.
Предоставленный код считывает цену, а затем применяет скидки p,10, f,300 или s,500,50, применяет каждую из них к цене и выводит 1000 -> 900.
Ваш код должен находиться в файлах Discount.kt, PercentOff.kt, FixedOff.kt и SpendMore.kt. В файле Main.kt находится предоставленный код ввода/вывода, и его нельзя редактировать.
Попробуйте сами
fun main() {
// Предоставленный код ввода/вывода: оставьте его как есть
val input = generateSequence(::readLine).toList()
val price = input[0].toInt()
for (line in input.drop(1)) {
val p = line.split(",")
val discount: Discount = when (p[0]) {
"p" -> PercentOff(p[1].toInt())
"f" -> FixedOff(p[1].toInt())
else -> SpendMore(p[1].toInt(), p[2].toInt())
}
println("$price -> ${discount.apply(price)}")
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Классы и объекты
Работа с файламиОпределение классовПервичные конструкторыБлоки init и валидацияВторичные конструкторыПовторение — читательский билет4Наследование
Открытые классы и подклассыПереопределение членовsuper и конструкторыАбстрактные классыПовторение — каталог сотрудников7Специальные классы
Классы данныхКлассы перечисленийЗапечатанные иерархииequals, hashCode, toStringПовторение — трекер заказов10Паттерны проектирования. Часть 2
Делегирование с помощью byПаттерн декораторПаттерн строительПаттерн состояние2Свойства и инкапсуляция
Пользовательские аксессорыМодификаторы видимостиПриватные сеттерыlazy и lateinitПовторение — банковский счёт5Интерфейсы
Объявление интерфейсовМетоды по умолчаниюМножественные интерфейсыИнтерфейсы как типыПовторение — способы оплаты8Обобщения
Обобщённые функцииОбобщённые классыОграничения типовВариативность с in и outПовторение — очередь с приоритетом11Проект: Управление библиотекой
Книги и читателиВыдача книг3Объекты и компаньоны
Объявления объектовОбъекты-компаньоныКонстанты с constОбъектные выраженияПовторение — билетная касса6Полиморфизм
Динамическая диспетчеризацияПроверка типов и умные приведения типовwhen с типамиФункции-расширенияПовторение — смотритель зоопарка9Паттерны проектирования. Часть 1
Паттерн «Одиночка»Фабричный паттернПаттерн «Наблюдатель»Паттерн «Стратегия»Потренируйтесь самостоятельно: Песочница Kotlin