Reputation: 4163
In my class I have a list containing TopicNodes. The nodes of those list should extend from the Message class. In the method findNode
within the list of nodes there is searched for a node with a specific topic and if it matches it is being returned. The Java compiler complains about the TopicNode being converted to a TopicNode of type T
as it is possible that it is not of type T
. What is the best way to solve this?
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
}
Upvotes: 1
Views: 167
Reputation: 18627
I suspect this should parameterise the class, not the method.
The code in the question must come from a class, which isn't shown. Each instance of that class holds a list of nodes. And to judge from the method, each class holds nodes of a particular message type. But the method a) requires the caller to know in advance which type, and b) allows it to be called on the same instance for different types, which would make no sense!
Instead, you can tell the compiler that each instance holds nodes of a particular type, by giving a type parameter on the class, instead of the method:
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
}
}
That way, the method already knows what type its nodes are, and can return them directly.
In fact, the class could then be simplified to:
class MyClass<T : Message> {
private val nodes: MutableList<TopicNode<T>> = ArrayList()
private fun findNode(topic: String)
= nodes.find{ it.topic == topic }
}
Upvotes: 2
Reputation: 3013
Putting this out there, just in case it fits the OP's use-case and they don't really need the 'extends generics' on their list. Sometimes you don't see the forest for the trees
private val nodes: MutableList<TopicNode<Message>>
private fun findNode(topic: String) = nodes.firstOrNull{ it.topic == topic}
If TopicNode is covariant then e.g. a TopicNode<MyMessage>
could be added to nodes
otherwise compiler would produce an error.
How do i know if TopicNode is covariant? It is covariant if TopicNode is declared as:
class TopicNode<Out T> {
It comes with a set of restrictions on that class. For more information on that topic https://kotlinlang.org/docs/reference/generics.html#variance
I do find the question from the comments interesting: how to define that the objects in a list should extend from another class? Not sure whether that is possible without going the route gidds did in their answer
Upvotes: 1
Reputation: 4163
I think I found the solution myself. You can use .filterIsInstance<T>()
for a list filtering the items of type T
in the list.
This leads to the following solution:
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
}
It checks for items in the list being of type TopicNode<T>
where T
extends from Message
.
Upvotes: 0