altavir
03/25/2018, 6:37 AMT
and it could produce other nodes that could possibly extend T
. Then I have a builder object for that tree that could consume T
or something extending T:
class Tree<T>{
fun getNode(name: String): Tree<T>
}
class Builder<T>{
fun setNode(name: String, tree: Tree<T>)
}
In first case I can possibly work without variance at all, but in second case I need some kind of variance to be able to put nodes of something: T
inside. How should I do it? Just putting in
variance in class header does not work.kingsley
03/25/2018, 9:11 AMTree
is a producer, you should add the out
variance propagate this information.
Builder on the other hand is a consumer, which will then work with in
.
class Tree<out T>{
fun getNode(name: String): Tree<T> = TODO()
}
class Builder<in T> {
fun setNode(name: String, tree: Tree<T>): Unit = TODO()
}
If however, you do not apply any variance info on Tree<T>, it will default to invariant, then applying in
to Builder will be an error, since T occurs as both co and contra variant according to Tree’s definitionaltavir
03/25/2018, 9:17 AMT
declared as out
but used in invariant position. While I am producing a T
object everything is fine, but if I want Tree<T>
it does not work.
In second case I get the same error and furthermore, I can't build the tree of type Tree<T>
from the builder since it is out operation.kingsley
03/25/2018, 9:32 AMTree
also needs to consume some kind of T right?
That means Tree<T> should indeed be invariantaltavir
03/25/2018, 9:34 AMfun getNode(name: String): Tree<T>
creates a error for reasons I do not fully understand. The builder obviously needs to somehow produce the result, so it is a litle bit more complicated.kingsley
03/25/2018, 9:38 AMclass Tree<out T>{
fun getNode(name: String): Tree<T> = TODO()
}
Builder on the other hand, should be invariant, since it will eventually produce a Tree<T>
as well.
On the other hand, you can leave both declarations invariant, and revert to use site variance. So, the builder method will look like:
fun setNote(name: String, tree: Tree<out T>)
altavir
03/25/2018, 9:40 AMout T
and not in T
? It is consumed after all.kingsley
03/25/2018, 9:42 AMout
is being set on Tree and not the builderaltavir
03/25/2018, 9:44 AMextends
, but still it breaks the logic somehow.kingsley
03/25/2018, 10:00 AMTree<out T>
simply asserts that you won’t be able to use any Tree#consume(T)
methods in that setNode
.
If the Builder were invariant, then you can apply in/out to Tree<T> parameter as necessary.
However, if the builder itself were to be a consumer, then the only assurance there is, is that t will at least be a type of the upper limit of T, automatically forcing it to be Tree<out T>
Not sure if this explains it clearlyaltavir
03/25/2018, 10:01 AMfun nodeStream(): Stream<Tree<T>>
to work.kingsley
03/25/2018, 11:01 AMaltavir
03/25/2018, 11:02 AMkingsley
03/25/2018, 11:05 AMaltavir
03/25/2018, 11:05 AMkingsley
03/25/2018, 11:11 AMilya.gorbunov
03/25/2018, 10:30 PMStream
is a java interface, therefore it doesn't contain any declaration-site variance info, so it is invariant.altavir
03/26/2018, 8:26 PM