Kotlin反射中不同类引用访问属性报错:期望Nothing却传入Person
反射读取属性报类型不匹配的原因及解决方法
原因分析
Person::class获取的是类的静态KClass实例,类型为KClass<Person>,无协变修饰符,对应的memberProperties返回Collection<KProperty1<Person, *>>,调用get(person)时参数类型完全匹配。person::class获取的是实例的运行时KClass,由于Kotlin的类型安全机制,其类型为KClass<out Person>(协变类型)——编译器无法确定person是否是Person子类的实例,因此用out标记该类型只能用于读取。KProperty1的定义为interface KProperty1<in T, out V>,其中第一个类型参数T是逆变的(in修饰)。当T为out Person时,逆变参数的合法输入类型被限制为Nothing(协变类型无法作为逆变参数的输入,编译器找不到符合要求的类型,只能退化为Nothing),这就导致调用get(person)时出现“期望Nothing,但传入了Person”的类型不匹配错误。
解决方案
方法1:强转KClass类型
将协变的KClass<out Person>强转为KClass<Person>,消除类型限制:
@Suppress("UNCHECKED_CAST") val b = person::class as KClass<Person> val personBfirstname = b.memberProperties.find { it.name == "firstname" }?.get(person)
添加@Suppress("UNCHECKED_CAST")可消除未检查转换警告,若确定person的实际类型就是Person而非子类,该方式安全可靠。
方法2:通过Java类转换为KClass
利用实例的Java类再转回Kotlin KClass,直接得到KClass<Person>:
val b = person.javaClass.kotlin val personBfirstname = b.memberProperties.find { it.name == "firstname" }?.get(person)
此方式无需强转,适合明确实例类型的场景。
方法3:强转属性类型
若不想修改b的类型,可对找到的属性进行强转:
val personBfirstname = (b.memberProperties.find { it.name == "firstname" } as KProperty1<Person, *>?)?.get(person)
内容的提问来源于stack exchange,提问作者Mat
相关产品推荐
相关产品推荐

