Как исправить эту непроверенную ошибку при использовании дженериков? - PullRequest
1 голос
/ 18 апреля 2019

В моем классе у меня есть список, содержащий TopicNodes. Узлы этого списка должны выходить из класса Message. В методе findNode в списке узлов выполняется поиск узла с определенной темой, и, если он совпадает, он возвращается. Компилятор Java жалуется на то, что TopicNode конвертируется в TopicNode типа T, поскольку возможно, что он не имеет типа T. Каков наилучший способ решить эту проблему?

private val nodes: MutableList<TopicNode<*>>

init {
    this.nodes = ArrayList()
}

private fun <T : Message> findNode(topic: String): TopicNode<T>? {
    for (node in nodes) {
        if (node.topic == topic) {
            return node as TopicNode<T> // Unchecked cast: TopicNode<*> to TopicNode<T>
        }
    }
    return null
}

Ответы [ 3 ]

2 голосов
/ 18 апреля 2019

Я подозреваю, что это должно параметризовать класс, а не метод.

Код в вопросе должен быть из класса, который не показан. Каждый экземпляр этого класса содержит список узлов. И, судя по методу, каждый класс содержит узлы определенного типа сообщения. Но метод a) требует, чтобы вызывающая сторона заранее знала, какой тип, и b) позволяет вызывать его в одном и том же экземпляре для разных типов, что не имеет смысла!

Вместо этого вы можете указать компилятору, что каждый экземпляр содержит узлы определенного типа, указав параметр типа в классе вместо метода:

class MyClass<T : Message> {
    private val nodes: MutableList<TopicNode<T>>

    init {
        this.nodes = ArrayList()
    }

    private fun findNode(topic: String): TopicNode<T>? {
        for (node in nodes) {
            if (node.topic == topic) {
                return node // No need to cast
            }
        }
        return null
    }
}

Таким образом, метод уже знает, к какому типу относятся его узлы, и может возвращать их напрямую.

Фактически, класс можно упростить до:

class MyClass<T : Message> {
    private val nodes: MutableList<TopicNode<T>> = ArrayList()

    private fun findNode(topic: String)
        = nodes.find{ it.topic == topic }
}
1 голос
/ 18 апреля 2019

Выкладывая это, на тот случай, если оно соответствует сценарию использования ОП, и им не нужны «расширенные дженерики» в их списке.Иногда вы не видите леса для деревьев

private val nodes: MutableList<TopicNode<Message>>

private fun findNode(topic: String) = nodes.firstOrNull{ it.topic == topic}

Если TopicNode является ковариантным, то, например, TopicNode<MyMessage> может быть добавлено к nodes, иначе компилятор выдаст ошибку.

Как я могу узнать, является ли TopicNode ковариантным?Это ковариантно, если TopicNode объявлен как:

class TopicNode<Out T> {

Он поставляется с набором ограничений для этого класса.Для получения дополнительной информации по этой теме https://kotlinlang.org/docs/reference/generics.html#variance

Мне действительно интересен вопрос из комментариев: как определить, что объекты в списке должны выходить из другого класса? Неуверен, возможно ли это, не пройдя маршрут, который Гидс сделал в своем ответе

0 голосов
/ 18 апреля 2019

Я думаю, что нашел решение сам.Вы можете использовать .filterIsInstance<T>() для списка, фильтрующего элементы типа T в списке.

Это приводит к следующему решению:

 private fun <T : Message> findNode(topic: String): TopicNode<T>? {
    val messageNodes: List<TopicNode<T>> = nodes.filterIsInstance<TopicNode<T>>()
    for (node in messageNodes) {
        if (node.topic == topic) {
            return node
        }
    }
    return null
}

Проверяет элементы в списке типа TopicNode<T>, где T начинается с Message.

...