Обобщённые классы
Часть раздела Объектно-ориентированное программирование путешествия по Kotlin на Coddy. Урок 38 из 57.
У класса также могут быть параметры типа. Box<T> хранит значение любого типа, а каждый экземпляр фиксирует тип: Box<Int> принимает только значения типа Int:
class Box<T>(var value: T) {
fun describe() = "box with $value"
}Внутри main:
val numberBox = Box(42)
val textBox = Box("hello")
numberBox.value += 1
println(numberBox.describe())
println(textBox.describe())
// numberBox.value = "x" // ошибка: String не является IntВывод:
box with 43
box with helloКонтейнеры — это типичные обобщённые классы. Одна реализация Stack<T> работает с элементами любого типа, а компилятор проверяет, что в неё помещается и что из неё извлекается:
class Stack<T> {
private val items = mutableListOf<T>()
val size: Int
get() = items.size
fun push(item: T) { items.add(item) }
fun pop(): T? = items.removeLastOrNull()
fun peek(): T? = items.lastOrNull()
}Внутри main:
val stack = Stack<String>()
stack.push("a")
stack.push("b")
println(stack.pop())
println(stack.peek())
println(stack.size)Вывод:
b
a
1Аргумент типа выводится из аргументов конструктора, если они есть, как в Box(42). В противном случае укажите его: Stack<String>(). Экземпляры с разными аргументами типа являются разными типами, поэтому Stack<String> никогда не смешивается с Stack<Int>. Функции обобщённого класса также могут добавлять собственные параметры типа:
class Box<T>(val value: T) {
fun <R> map(f: (T) -> R): Box<R> = Box(f(value))
}Внутри main:
val length = Box("kotlin").map { it.length }
println(length.value + 1)Результат:
7Обобщённые интерфейсы подчиняются тем же правилам. Класс может реализовать обобщённый интерфейс или интерфейс с одним конкретным аргументом типа:
interface Repository<T> {
fun add(item: T)
fun all(): List<T>()
}
class MemoryRepository<T> : Repository<T> {
private val items = mutableListOf<T>()
override fun add(item: T) { items.add(item) }
override fun all() = items.toList()
}
class TagRepository : Repository<String> {
private val tags = sortedSetOf<String>()
override fun add(item: String) { tags.add(item.lowercase()) }
override fun all() = tags.toList()
}Внутри main:
val ids = MemoryRepository<Int>()
ids.add(3); ids.add(1)
val tags = TagRepository()
tags.add("Kotlin"); tags.add("JVM")
println("${ids.all()} ${tags.all()}")Вывод:
[3, 1] [jvm, kotlin]Задание
ЛегкоНапишите обобщённый класс History<T>(limit), который запоминает последние элементы в количестве limit. add(item) добавляет элемент и удаляет самый старый, когда элементов становится больше, чем limit. latest — это самый новый элемент или null, а all() возвращает элементы от самого старого к самому новому в виде списка только для чтения. Затем напишите расширение History<Int>.average(), которое существует только для историй чисел и возвращает целочисленное среднее сохранённых чисел (0, если история пуста).
Предоставленный код считывает лимит, строку со словами и строку с числами. Он заполняет History<String> и History<Int>, а затем выводит сохранённые слова, последнее слово, сохранённые числа, их среднее и empty latest: null.
Ваш код должен находиться в History.kt. В Main.kt содержится предоставленный код ввода/вывода, и его нельзя изменять.
Попробуйте сами
fun main() {
// Предоставленный код ввода/вывода: оставьте его как есть
val input = generateSequence(::readLine).toList()
val limit = input[0].toInt()
val words = History<String>(limit)
for (w in input[1].split(" ")) words.add(w)
val numbers = History<Int>(limit)
for (n in input[2].split(" ")) numbers.add(n.toInt())
println("words: ${words.all()}")
println("latest word: ${words.latest}")
println("numbers: ${numbers.all()}")
println("average: ${numbers.average()}")
println("empty latest: ${History<Char>(3).latest}")
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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