在将Java代码重写为Kotlin的过程中,我需要定义列表的对象类型。问题是它未知,并且可以包含不同类型的对象。情况:
在Java类中,我定义了一个列表,其中包含TopicNode
个对象:private List<TopicNode> nodes
。这些对象可以包含T extends Message
类型的消息。消息的类型可以为Boolean
,Int32
,String
等。节点看起来像:
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
的类型将是未知的,它也可以变化。
既然是这种情况,我该如何应对科特林的这种情况?可能吗
非常感谢!
答案 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或反之亦然的情况下,这称为不变性。这里没有子类型与父类型的关系。