Котлин: Создание подклассов универсальных типов наследует функции и ограничивает доступные типы вывода

У меня есть следующие классы 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 описывают определенный член этой коллекции. Подклассы BigCollection и SmallCollection определяют, какой у нас размер коллекции. Моя идея состоит в том, чтобы добавить сюда больше, в зависимости от того, насколько сложно работать с коллекциями.

Можно представить следующие примеры

Красный может быть экземпляром / объектом типа Member

Persons может быть подтипом типа SmallCollection

Цвет может быть подтипом типа BigCollection и быть по существу бесконечным. Это означает, например, что функция, содержащая (Member), никогда не может возвращать false, только Unsure или, возможно, True, если она нашла это и выдает исключение после определенного времени ожидания.

Просто у вас есть базовое представление о том, что я пытаюсь сделать.

Обратите внимание, как стирание типа JVM заставляет меня использовать рекурсивные обобщения, если я хочу иметь доступ к типу MyCollection как во время компиляции, так и во время выполнения.

Мы можем видеть, что это довольно безопасный тип. Мы знаем, что mapToOtherDomain вернет E, который является подтипом MyCollection, и он будет того же типа, что и E в функции (Member) -> Member), которую мы вставили.

Теперь, что было бы замечательно, было бы, если бы подклассы 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: мы создаем метод с более конкретной сигнатурой и делегируем его.

Другие вопросы по тегам