Как бороться с этим преобразованием общего списка из Java в Kotlin? - PullRequest
0 голосов
/ 23 апреля 2019

В процессе переписывания Java-кода в Kotlin мне нужно определить тип объекта списка. Дело в том, что неизвестно и может содержать объекты различного типа. Ситуация:

В моем классе Java я определил список с TopicNode объектами: private List<TopicNode> nodes. Эти объекты могут содержать сообщения типа T extends Message. Сообщения могут быть типа Boolean, Int32, String и т. Д. Узел выглядит так:

TopicNode.java

class TopicNode<T extends Message> extends AbstractNodeMain {
    // Implementation
}

Javaclass, содержащий этот список, выглядит так:

TopicControllerImpl.java

public class TopicControllerImpl implements TopicController {

    private List<TopicNode> nodes;

    @Override
    public void subscribe(String topic, Class type, Messenger subscriber) {
        TopicNode node = findOrCreateNode(topic, type);
        node.addListener(subscriber);
    }

    private <T extends Message> TopicNode<T> findNode(String topic) {
        for (TopicNode node : nodes) {
            if (node.getTopic().equals(topic)) {
                return (TopicNode<T>) node;
            }
        }
        return null;
    }

    private <T extends Message> TopicNode findOrCreateNode(String topic, Class<T> type) {
        TopicNode<T> node = findNode(topic);
        if (node != null) {
            return node;
        }
        // If node doesn't exist yet, create it, run it and add it to the collection
        node = new TopicNode<>(topic, type);
        executor.execute(node, configuration);
        nodes.add(node);
        return node;
    }
}

Когда я пытаюсь определить такой список в Kotlin (private val nodes: ArrayList<TopicNode> = ArrayList()), компилятор говорит: One type argument expected for class TopicNode<T : Message>.

Чтобы решить эту проблему, вы можете определить класс и список в Kotlin следующим образом:

TopicControllerImpl.kt

class TopicControllerImpl<T : Message>(/** args */) : TopicController<T> {
    private val nodes: ArrayList<TopicNode<T>> = ArrayList()

    override fun subscribe(topic: String, type: Class<T>, subscriber: Messenger) {
        val node = findOrCreateNode(topic, type)
        node.addListener(subscriber)
    }

    private fun findNode(topic: String): TopicNode<T>? {
        return nodes.firstOrNull { it.topic == topic }
    }

    private fun findOrCreateNode(topic: String, type: Class<T>): TopicNode<T> 
    {
        var node = findNode(topic)
        if (node != null) {
            return node
        }
        // If node doesn't exist yet, create it, run it and add it to the collection
        node = TopicNode(topic, type)
        executor.execute(node, configuration)
        nodes.add(node)

        return node
    }
}

Проблема в том, что вам нужно определить тип T, который используется для списка в TopicControllerImpl, в то время как это неизвестно и не требуется в Java. Неизвестно, каким будет тип Message, и он также может варьироваться.

Так как это так, как я могу справиться с этой ситуацией в Котлине? Это вообще возможно?

Заранее большое спасибо!

Ответы [ 2 ]

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

You should never use raw types in Java in the first place, see wildcards or their Kotlin equivalent - @Moira

Я решил эту проблему, избегая использования необработанного типа T.Поэтому я удалил <T : Message> в определении класса;Это означает, что везде, где используется T, объект, заполняющий это, должен расширяться от Message.

Допустим, у нас есть объект Monkey, который является подклассом абстрактного класса Animal.

В kotlin, в отличие от Java, можно добавить список Monkeys вСписок, который требует животных.Итак:

List<Monkey> monkeys = getMonkeys()
List<Animal> animals = monkeys      // Will compile with Kotlin; will not compile for Java

Эта способность сделать это вызвана так называемым covariance.

Больше информации о дженериках и дисперсиях: https://proandroiddev.com/understanding-generics-and-variance-in-kotlin-714c14564c47

В нем говорится, что невозможно включить обезьян в список животных, потому что:

Этопотому что дженерики в Java игнорируют отношение типа к подтипу между его компонентами.В случае, когда List не может быть назначен List, и наоборот, это называется инвариантностью.Здесь нет отношения подтип к супертипу.

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

Вот разница между универсальным и групповым символами. Например

private val nodes: ArrayList<TopicNode<T>> = ArrayList()

означает, что у меня будет массив этого универсального класса с данным типом T. в то время как

private val nodes: ArrayList<TopicNode<*>> = ArrayList()

означает, что у меня будет массив этого универсального класса со смешанным типом 1011 *.

Если вы хотите использовать первое, измените определение class , но если вы хотите второе, измените объявление array .

...