如何通过反射获取Kotlin作用域函数中的参数属性名
解决反射作用域函数同类型多属性匹配错误的问题
问题背景
我实现了一个Reflect类的作用域函数,用于通过反射将对象的status属性设置为InProgress实例。当前实现存在缺陷:当目标类包含多个同类型属性(如status和status2)时,反射端通过first()匹配属性会返回错误的属性。要求必须保留on { propertyName }的调用语法,不能改为直接传递KProperty的方式。
现有代码
调用示例
Reflect(myExampleInstance) { on { status }.then(InProgress) }
存在问题的反射实现
inline fun <reified R> on( p: T.() -> R, ): OnGoingReflect { val declaredMember: KProperty1<out T, *> = instance::class.declaredMemberProperties.first { it.returnType.jvmErasure.java == R::class.java && it.getter.call(instance) == p(instance) } return OnGoingReflect(instance, declaredMember.name) }
解决方案
核心思路是利用Kotlin内联函数的字节码特性,直接从on { propertyName }的lambda表达式中解析出目标属性的名称,而非依赖类型和属性值的匹配。这种方式可以精准定位到目标属性,完全避免同类型多属性的匹配冲突。
修正后的实现(依赖ASM字节码解析库)
import org.objectweb.asm.ClassReader import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.MethodInsnNode import org.objectweb.asm.tree.MethodNode inline fun <reified R> on( p: T.() -> R, ): OnGoingReflect { // 获取lambda的字节码类对象 val lambdaClass = p.javaClass val classReader = ClassReader(lambdaClass.name) val classNode = ClassNode() classReader.accept(classNode, 0) // 找到lambda的invoke方法(Kotlin lambda的核心执行方法) val invokeMethod = classNode.methods.first { it.name == "invoke" } as MethodNode // 从字节码中提取属性getter的调用指令 val getterCall = invokeMethod.instructions.iterator().asSequence() .filterIsInstance<MethodInsnNode>() .first { it.opcode == org.objectweb.asm.Opcodes.INVOKEVIRTUAL } // 从getter方法名转换为Kotlin属性名 val propertyName = when { getterCall.name.startsWith("get") -> getterCall.name.substring(3).replaceFirstChar(Char::lowercase) getterCall.name.startsWith("is") -> getterCall.name.substring(2).replaceFirstChar(Char::lowercase) else -> getterCall.name // 兼容特殊命名的属性 } // 直接通过属性名匹配目标KProperty val targetProperty = instance::class.declaredMemberProperties.first { it.name == propertyName } return OnGoingReflect(instance, targetProperty.name) }
补充说明
- 依赖准备:需要在项目中添加ASM库依赖,例如Gradle配置:
implementation 'org.ow2.asm:asm:9.5' - 可靠性:由于
on是内联函数,lambda会被直接嵌入调用代码中,字节码解析的结果稳定可靠,不会受属性值变化的影响。 - 混淆兼容:如果项目开启了代码混淆,需要确保反射操作的类和属性不被混淆(可通过ProGuard规则配置)。
内容的提问来源于stack exchange,提问作者Oğuzhan Soykan
相关产品推荐
相关产品推荐

