Kotlin高效判断集合对象仅单个属性非空的实现方案
Kotlin中高效判断集合对象仅单个指定属性非null的方案
针对数据类属性较多时过滤条件冗余的问题,以下是几种通用且可维护的实现方式:
方案一:基于Kotlin反射的通用扩展函数(推荐)
利用Kotlin反射自动获取数据类的所有属性,无需手动维护属性列表,新增属性时完全不用修改过滤逻辑。为避免字符串拼写错误,使用属性引用而非字符串指定目标属性,兼顾类型安全。
实现步骤
首先在Gradle中添加kotlin-reflect依赖:
implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
然后定义通用扩展函数:
import kotlin.reflect.KProperty1 import kotlin.reflect.full.declaredMemberProperties /** * 判断对象是否仅指定属性非null,其余所有属性均为null * @param property 目标属性的引用(如Person::age) */ fun <T> T.hasOnlyNonNullProperty(property: KProperty1<T, *>): Boolean { val allProperties = this::class.declaredMemberProperties // 先检查目标属性是否非null val targetValue = property.get(this) ?: return false // 检查其余所有属性是否均为null return allProperties.filter { it != property }.all { it.get(this) == null } }
使用示例
data class Person(val name: String?, val age: Int?, val address: String?, val phone: String?) val people = listOf( Person("Mary", 20, "Street 111", null), Person(null, 22, null, null), // 仅age非null Person(null, 21, "Street 123", null), Person(null, null, null, "123456") // 仅phone非null ) // 过滤仅age非null的元素 val filteredAgeOnly = people.filter { it.hasOnlyNonNullProperty(Person::age) } // 过滤仅phone非null的元素 val filteredPhoneOnly = people.filter { it.hasOnlyNonNullProperty(Person::phone) } println(filteredAgeOnly) // 输出 [Person(name=null, age=22, address=null, phone=null)] println(filteredPhoneOnly) // 输出 [Person(name=null, age=null, address=null, phone=123456)]
优缺点
- ✅ 新增属性无需修改过滤逻辑,可维护性极强
- ✅ 类型安全,避免字符串拼写错误
- ❌ 反射存在轻微性能开销,适合绝大多数非性能敏感场景
方案二:手动维护属性列表(性能优先)
如果对性能要求极高(如百万级数据循环处理),可以手动维护数据类的属性值列表,避免反射开销。
实现代码
data class Person(val name: String?, val age: Int?, val address: String?, val phone: String?) { // 手动返回所有属性值的列表,新增属性时需同步更新此方法 fun getAllPropertyValues(): List<Any?> = listOf(name, age, address, phone) } // 过滤仅age非null的元素 val filteredAgeOnly = people.filter { it.age != null && it.getAllPropertyValues().count { value -> value != null } == 1 }
优缺点
- ✅ 无反射开销,性能最优
- ❌ 新增属性时必须手动更新
getAllPropertyValues方法,存在维护成本
方案三:代码生成(兼顾通用与性能)
对于大型项目,可使用KSP(Kotlin Symbol Processing)生成每个数据类的属性检查代码,既保留反射的通用性,又避免运行时反射开销。不过需要额外配置代码生成插件,适合属性频繁变更且对性能有要求的场景。
内容的提问来源于stack exchange,提问作者Muchtar
相关产品推荐
相关产品推荐

