为何出现Type mismatch类型推断错误?泛型赋值问题解析
1. 泛型默认是「不变」的
Kotlin中,泛型类型默认具备不变性:即使CustomRule是Rule的子类,Content<CustomRule>和Content<Rule>之间不存在任何继承关系——它们是完全独立的类型,直接赋值必然触发「类型不匹配」错误。
比如你代码中的这段错误:
var defaultRuleContent: Content<Rule> = createDefaultContent() val customRuleContent: Content<CustomRule> = createSpecialContent() defaultRuleContent = customRuleContent // 类型不匹配报错
没有泛型修饰符时,这种赋值在类型系统里是不被允许的,因为两个Content实例的泛型参数虽有继承关系,但容器本身没有继承关联。
2. 协变(out修饰符):支持泛型的「向上转型」
你已经在Content类上添加了out T: Rule,按协变规则,Content<CustomRule>应该可以赋值给Content<Rule>,但报错的问题出在**var变量的可写性**上。
out的核心约束是:泛型参数T只能作为「输出」(比如返回值、只读属性),不能作为「输入」(比如方法参数、可写属性)。当你用var定义defaultRuleContent时,这个变量是可修改的——后续可能给它赋值一个普通Content<Rule>实例(比如包含Rule而非CustomRule的对象),这会破坏协变的类型安全:如果变量原本指向Content<CustomRule>,后续赋值的Content<Rule>无法满足CustomRule的类型要求,会引发潜在的类型转换异常。
解决方法非常简单:将var改为val(只读变量),此时编译器能保证变量不会被重新赋值,协变类型的赋值就完全合法:
val defaultRuleContent: Content<Rule> = createSpecialContent() // 编译通过
3. 协变的本质逻辑
out T是在告诉编译器:这个泛型类只会向外暴露T类型的实例(比如你的Content只有只读的val rule: T,没有接收T作为参数的方法)。因此,把Content<SubType>当成Content<SuperType>使用是安全的——从容器中取出的SubType实例,总能向上转型为SuperType。
4. 逆变(in修饰符):反向的类型兼容
如果泛型类的作用是接收T类型的实例(比如一个只处理T的处理器类),可以用in修饰符实现逆变:此时Content<SuperType>可以赋值给Content<SubType>。示例:
class RuleProcessor<in T: Rule> { fun process(rule: T) { /* 处理逻辑 */ } } // 合法:能处理所有Rule的处理器,必然能处理CustomRule val customProcessor: RuleProcessor<CustomRule> = RuleProcessor<Rule>()
逆变的核心是:能处理父类型的逻辑,一定能处理子类型,符合类型安全要求。
内容的提问来源于stack exchange,提问作者question

