为何Kotlin密封类JsonValue的构造参数要使用out T?
out T而不是in T? 先纠正一个关键误区:协变(out)和逆变(in)的规则是针对类的对外成员接口(比如成员函数、属性的读写),而不是构造器参数。构造器是创建实例时的初始化逻辑,不属于类对外暴露的可调用接口,所以它接收T类型参数这件事,和协变的规则不冲突。
先看代码里的核心需求
在main函数中,我们把JsonValue<String>、JsonValue<Number>这些不同泛型参数的子类实例,赋值给了JsonValue<Any?>类型的变量pi。这种泛型类型的向上转型,正是out T带来的协变能力:当类声明为out T时,JsonValue<String>会被视为JsonValue<Any?>的子类型(因为String是Any?的子类型),这样赋值才合法。
如果换成in T(逆变),情况会完全相反:JsonValue<Any?>会成为JsonValue<String>的子类型,这时候你根本不能把JsonValue<String>赋值给JsonValue<Any?>的变量,这就是为什么替换后编译失败的原因。
再拆解out T的真实含义
out T的本质是标记这个类是T类型的生产者:它对外暴露的接口只会输出T(比如返回T、提供只读的T类型属性),不会输入T(比如没有接收T作为参数的成员函数、没有可写的T类型属性)。
回到你的代码:
val value: T是只读属性,对外提供的是一个返回T类型值的getter,属于“输出T”,完全符合out T的要求。- 构造器接收T参数只是初始化这个只读属性,不是类对外的可调用接口,所以不受
out规则的限制——协变管的是类创建后能做什么,不是创建时需要什么。
为什么不能用in T
in T标记类是T类型的消费者:它对外暴露的接口只会输入T(比如接收T作为参数的成员函数),不会输出T。如果给JsonValue用in T,意味着这个类应该是接收T类型数据来处理,而不是对外提供T类型数据,这和代码里JsonValue存储并对外提供value的定位完全不符,同时也破坏了main函数中需要的向上转型能力。
内容的提问来源于stack exchange,提问作者ceving

