Котлин: Создание подклассов универсальных типов наследует функции и ограничивает доступные типы вывода
У меня есть следующие классы Kotlin, реализующие что-то похожее на интерфейс Collections или набор, который содержит несколько элементов.
abstract class MyCollection<C: MyCollection<C>> {
abstract fun contains(e: Member<C>): Member<CollectionOfBooleanValues>
//Function that determines if a Member<C> is present in this collection.
// Maps to a collection of boolean values (True, False and Unsure)
abstract fun <E: MyCollection<E>> mapToOtherCollection(f: (Member<C>) -> Member<E>): E
//Function that return a collection of the element that are created from
//a mapping of this collection through a function f.
//This could for example be the owners of the things of this collection, provided we
//have a Persons class extends MyCollection. Or a collection of all colors of the things in this collection.
}
abstract class BigCollection<C: BigCollection<C>>: MyCollection<C>() {
override fun contains(e: Member<C>): Member<CollectionOfBooleanValues> = Unsure
abstract override fun <E: MyCollection<E>> mapToOtherCollection(f: (Member<C>) -> Member<E>): E
}
abstract class SmallCollection<C: SmallCollection<C>>(val choices: List<Member<C>>): BigCollection<C>() {
override fun contains(e: Member<C>): Member<CollectionOfBooleanValues> =
if(choices.contains(e))
True
else
False
}
abstract class Persons<D: Persons<D>>: MyCollection<D>() {
override fun contains(e: Member<D>): Member<CollectionOfBooleanValues> {
return True
}
}
abstract class Member<D: MyCollection<D>>(sym: String, val collectionType: D) {
}
object CollectionOfBooleanValues: SmallCollection<CollectionOfBooleanValues>(choices = listOf(True, False, Unsure)){
override fun <E: MyCollection<E>> mapToOtherCollection(f: (Member<CollectionOfBooleanValues>) -> Member<E>): E =
throw RuntimeException("Not implemented");
}
object True: Member<CollectionOfBooleanValues>("true", CollectionOfBooleanValues)
object False: Member<CollectionOfBooleanValues>("false", CollectionOfBooleanValues)
object Unsure: Member<CollectionOfBooleanValues>("unsure", CollectionOfBooleanValues)
Таким образом, экземпляры одного из подклассов MyCollection описывают некоторый набор, а экземпляры класса Member
Можно представить следующие примеры
Красный может быть экземпляром / объектом типа Member
Persons может быть подтипом типа SmallCollection
Цвет может быть подтипом типа BigCollection
Просто у вас есть базовое представление о том, что я пытаюсь сделать.
Обратите внимание, как стирание типа JVM заставляет меня использовать рекурсивные обобщения, если я хочу иметь доступ к типу MyCollection
Мы можем видеть, что это довольно безопасный тип. Мы знаем, что mapToOtherDomain вернет E, который является подтипом MyCollection
Теперь, что было бы замечательно, было бы, если бы подклассы MyCollection могли переопределить функцию mapToOtherCollection, чтобы размер отражался в статической сигнатуре функции. Функция f является взаимно-однозначным отображением, поэтому, если мы сопоставляем smallCollection типа A с B I, мы хотим, чтобы выходные данные были smallCollection of B.
Я чувствую, что это должно быть возможно, тем более что это почти возможно в Java, а Kotlin должен быть расширением в системе универсальных типов Java. Я не могу заставить его работать, хотя Я хочу переопределить, но все же ограничить тип возвращаемого значения (и, следовательно, также тип ввода)
Я играл с методами подписи, как это:
abstract class BigCollection<C: BigCollection<C>>: MyCollection<C>() {
abstract override fun <E: BigCollection<E>> mapToOtherCollection(f: (Member<C>) -> Member<E>): E
}
что дает ошибку компилятора, что он не переопределяет предыдущий метод, и вот так
abstract class MyCollection<C: MyCollection<C>> {
abstract fun <E: MyCollection<E>> mapToOtherCollection(f: (Member<C>) -> Member<E>): MyCollection<E>
}
abstract class BigCollection<C: BigCollection<C>>: MyCollection<C>() {
abstract override fun <E: MyCollection<E>> mapToOtherCollection(f: (Member<C>) -> Member<E>): BigCollection<E>
}
который говорит, что аргумент типа выходит за пределы.
Как я могу просто ограничить типы ввода / вывода, чтобы лучше соответствовать подклассу универсального класса в этом примере? Было бы также замечательно, если бы компилятор знал, что вывод функции contains() в BigCollection будет статическим, а не динамическим.
Спасибо
1 ответ
Трудно сказать, поможет ли это или нет, но вы можете по крайней мере рассмотреть это как обходной путь:
abstract class BigCollection<C: BigCollection<C>>: MyCollection<C>() {
override fun <E: MyCollection<E>> mapToOtherCollection(f: (Member<C>) -> Member<E>): MyCollection<E> {
return this.mapToOtherCollection(f)
}
abstract fun <E: BigCollection<E>> mapToOtherCollection(f: (Member<C>) -> Member<E>): BigCollection<E>
}
То, что здесь происходит, похоже на методы "моста" JVM: мы создаем метод с более конкретной сигнатурой и делегируем его.