Как в Kotlin объявить generic класс, который одновременно наследуется от класса и реализует интерфейс?
Решение: используйте ключевое слово where для множественных ограничений
В Kotlin для объявления generic класса, который наследуется от класса И реализует интерфейс, используется конструкция where. Она позволяет задать несколько ограничений для одного параметра типа. Вот правильный синтаксис:
open class BaseClass
interface MyInterface
// Правильный вариант
class MyClass<T> where T : BaseClass, T : MyInterface {
fun process(item: T) {
// Теперь item имеет доступ к методам BaseClass и MyInterface
item.baseMethod()
item.interfaceMethod()
}
}
Как это работает
- Ключевое слово
whereставится после объявления параметра типа и перед телом класса. - Ограничения перечисляются через запятую:
T : BaseClass, T : MyInterface. - Порядок важен: сначала указывается класс (только один!), затем интерфейсы (их может быть несколько).
Пример с несколькими интерфейсами
open class Animal
interface Runnable
interface Swimmable
class AquaticAnimal<T> where T : Animal, T : Runnable, T : Swimmable {
fun perform(item: T) {
item.run()
item.swim()
}
}
Альтернативный синтаксис (для одного ограничения)
Если у вас только одно ограничение (только класс или только интерфейс), можно использовать более короткую запись:
// Только класс
class MyClass<T : BaseClass>
// Только интерфейс
class MyClass<T : MyInterface>
Почему не работает ваша "Попытка 2"
Запись class MyClass<T : BaseClass, MyInterface> воспринимается компилятором как два параметра типа: T (ограниченный BaseClass) и MyInterface (который трактуется как имя второго параметра, а не как ограничение). Это вызывает ошибку.
Что нельзя делать
- Нельзя наследоваться от нескольких классов — Kotlin не поддерживает множественное наследование классов, только от одного класса и нескольких интерфейсов.
- Нельзя использовать
&как в Java — Kotlin используетwhereвместо амперсанда.
Полный рабочий пример
open class Vehicle(val name: String)
interface Engine {
fun start()
}
interface Horn {
fun honk()
}
class Car<T> where T : Vehicle, T : Engine, T : Horn {
fun drive(item: T) {
println("Driving ${item.name}")
item.start()
item.honk()
}
}
// Используем
class MyCar(name: String) : Vehicle(name), Engine, Horn {
override fun start() = println("Engine started")
override fun honk() = println("Beep beep!")
}
fun main() {
val car = Car<MyCar>()
car.drive(MyCar("Tesla"))
// Вывод: Driving Tesla
// Engine started
// Beep beep!
}
Таким образом, ключевое слово where — это основной и самый гибкий способ задать множественные ограничения для generic параметров в Kotlin. Оно читается естественно и полностью заменяет Java-синтаксис с амперсандом.
Альтернативный подход: использование typealias для сложных ограничений
Если у вас часто встречается комбинация "класс + интерфейс", можно создать typealias, чтобы упростить чтение кода:
open class Entity
interface Savable
// Создаём псевдоним для ограничения
typealias SavableEntity<T> = T where T : Entity, T : Savable
// Используем
class Repository<T> where T : Entity, T : Savable {
fun save(item: T) { /* ... */ }
}
// Или даже так (если Kotlin версии 1.9+)
// class Repository<T : SavableEntity<T>> - пока не поддерживается напрямую
Когда это может быть полезно
- Когда одно и то же ограничение используется в нескольких классах
- Для улучшения читаемости кода с длинными цепочками ограничений
Однако помните: typealias не создаёт новый тип, а только даёт имя существующему ограничению. Это не заменяет where, а дополняет его для случаев с повторяющимися паттернами.
Ответов пока нет.
Войдите чтобы оставить ответ