如何处理从Java到Kotlin的一般列表转换?

时间:2019-04-23 08:44:13

标签: java android list generics kotlin

在将Java代码重写为Kotlin的过程中,我需要定义列表的对象类型。问题是它未知,并且可以包含不同类型的对象。情况:

在Java类中,我定义了一个列表,其中包含TopicNode个对象:private List<TopicNode> nodes。这些对象可以包含T extends Message类型的消息。消息的类型可以为BooleanInt32String等。节点看起来像:

  

TopicNode.java

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

包含此列表的Java类如下:

  

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 :(得分:0)

这是通用和通配符之间的区别。例如

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

意味着我将拥有一个具有该类型的数组,该类具有给定的类型T。而

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

意味着我将拥有一个具有 mixed 类型的通用类的数组。

如果要使用第一个,请更改您的 class 定义,但如果要使用第二个,请更改 array 声明。

答案 1 :(得分:-1)

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相比,可以将猴子列表添加到需要动物的列表中。所以:

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或反之亦然的情况下,这称为不变性。这里没有子类型与父类型的关系。