Kotlin反射:安全获取对象已存在属性的实现方案问询
Kotlin安全实现按属性名动态获取属性值
我希望实现一个通用方法,当对象存在指定名称的属性时获取其值(实际业务场景更复杂,此示例简化呈现核心问题)。当前的实现需要打破型变规则,想请教是否有安全的实现方式?
当前实现代码
fun <T : Any> T.valueFor(name: String): Any? { val clazz = this::class as KClass<T> // <-- 为规避型变问题的不安全转换 val prop = clazz.memberProperties.firstOrNull { name == it.name } ?: throw IllegalArgumentException("No property called '$name'") return prop.get(this) // <-- 没有上面的转换会出现编译错误 }
测试用例
@Test fun `retrieve property`() { open class Animal(val color: String) class Dog(val name: String, color: String) : Animal(color) val dog = Dog("Rocky", "Yellow") assertThat(dog.valueFor("name")).isEqualTo(dog.name) assertThat(dog.valueFor("color")).isEqualTo(dog.color) val animal: Animal = dog assertThat(animal.valueFor("name")).isEqualTo(dog.name) assertThat(animal.valueFor("color")).isEqualTo(dog.color) val diffAnimal = Animal("Orange") assertThrows<IllegalArgumentException> { diffAnimal.valueFor("name") } assertThat(diffAnimal.valueFor("color")).isEqualTo(diffAnimal.color) }
分析与安全实现方案
当前代码中this::class as KClass<T>的强制转换存在类型安全隐患:当对象是子类实例但被父类类型引用时,this::class返回的是子类的KClass,而T是父类类型,这种转换违反了型变规则,在复杂继承场景下可能引发意外错误。
方案一:安全转换属性接收者类型
去掉不安全的KClass强制转换,转而对属性的接收者类型做安全转换(由于属性来自当前对象的实际类型,该转换是安全的):
fun <T : Any> T.valueFor(name: String): Any? { val prop = this::class.memberProperties.firstOrNull { it.name == name } ?: throw IllegalArgumentException("No property called '$name'") @Suppress("UNCHECKED_CAST") return (prop as KProperty1<T, *>).get(this) }
方案二:使用call方法绕过类型限制
利用KCallable.call方法(接收Any?类型参数),无需显式类型转换即可调用属性的获取逻辑:
fun <T : Any> T.valueFor(name: String): Any? { val prop = this::class.memberProperties.firstOrNull { it.name == name } ?: throw IllegalArgumentException("No property called '$name'") return prop.call(this) }
这两种方案都保持了方法的通用性(无需提前知晓对象具体类型),同时避免了原代码中的不安全类型转换。
内容的提问来源于stack exchange,提问作者Kurt
相关产品推荐
相关产品推荐

