如何通过反射实现从父对象获取成员属性名的getMemberName函数
问题解答
Kotlin标准反射库没有直接通过值逆向获取成员路径的通用方案——因为值本身不携带任何上下文信息,同一个值可能对应多个不同的成员(比如两个属性值相同的场景),反射无法准确推导。
如果要实现可靠的getMemberName功能,推荐通过属性引用或lambda捕获访问路径的方式实现,而非传入值。以下是两种可行的实现方案:
方案1:使用属性引用(类型安全)
通过Kotlin的KProperty类型直接传递属性引用,支持嵌套属性的链式调用:
import kotlin.reflect.KProperty1 data class Inner(val prop: String) data class Source( val member: String, val innerMember: Inner ) // 获取单级属性名称 fun <T, R> getMemberName(property: KProperty1<T, R>): String = property.name // 扩展方法,支持嵌套属性拼接 fun <T, R, V> KProperty1<T, R>.dot(property: KProperty1<R, V>): KProperty1<T, V> { return object : KProperty1<T, V> { override val name: String get() = "${this@dot.name}.${property.name}" override val returnType = property.returnType override val annotations = emptyList() override fun get(receiver: T): V = property.get(this@dot.get(receiver)) } } fun main() { val demo = Source("asd", Inner("asd2")) val memberName = getMemberName(Source::member) println(memberName) // 输出: member val innerMemberName = getMemberName(Source::innerMember.dot(Inner::prop)) println(innerMemberName) // 输出: innerMember.prop }
方案2:使用Lambda捕获访问路径
通过内联Lambda捕获属性访问逻辑,解析Lambda的调用信息获取路径(注意:此方式依赖Kotlin反射的内部实现,版本变动可能影响兼容性):
import kotlin.reflect.full.valueParameters data class Inner(val prop: String) data class Source( val member: String, val innerMember: Inner ) inline fun <reified T, R> getMemberName(crossinline selector: T.() -> R): String { val lambda = selector as (T) -> R val function = lambda::class.java.getDeclaredField("function").apply { isAccessible = true }.get(lambda) val callable = function as kotlin.reflect.KFunction<*> // 从Lambda的toString输出中提取属性路径 return callable.toString() .substringAfterLast("-> ") .substringBefore(")") .replace("this.", "") } fun main() { val demo = Source("asd", Inner("asd2")) val memberName = getMemberName { it: Source -> it.member } println(memberName) // 输出: member val innerMemberName = getMemberName { it: Source -> it.innerMember.prop } println(innerMemberName) // 输出: innerMember.prop }
不推荐的方案:通过值遍历匹配
如果硬要通过值来实现,只能遍历所有成员和嵌套成员进行值匹配,但这种方式不可靠且性能差——遇到重复值时会返回第一个匹配的成员,无法保证正确性:
import kotlin.reflect.full.memberProperties fun getMemberName(source: Any, targetValue: Any): String? { val sourceClass = source::class // 遍历当前类的所有成员 for (prop in sourceClass.memberProperties) { val value = prop.get(source) if (value == targetValue) return prop.name // 递归遍历嵌套对象的成员(跳过基本类型和字符串) val returnType = prop.returnType.classifier as? Class<*> ?: continue if (!returnType.isPrimitive && returnType != String::class.java) { val nestedPath = getMemberName(value, targetValue) if (nestedPath != null) return "${prop.name}.$nestedPath" } } return null } fun main() { val demo = Source("asd", Inner("asd2")) println(getMemberName(demo, demo.member)) // 输出: member println(getMemberName(demo, demo.innerMember.prop)) // 输出: innerMember.prop // 存在值重复时出错 val demo2 = Source("asd2", Inner("asd2")) println(getMemberName(demo2, "asd2")) // 输出: member(而非预期的innerMember.prop) }
内容的提问来源于stack exchange,提问作者Nikita Chegodaev
相关产品推荐
相关产品推荐

