Kotlin泛型集合排序问题:SQL排序失效时的客户端排序实现
泛型集合排序解决方案
问题背景
我有一个用于过滤查询数据的Kotlin内联泛型函数findByFilterFunc,原代码依赖数据库方言实现排序,但当前数据库不支持该排序方式,只能在内存中对查询结果排序。尝试用KotlinsortedBy {}处理时代码无效,需要实现针对未知泛型类型的内存排序。
原函数代码:
inline fun <reified T> findByFilterFunc(filter: String, em: EntityManager, offset: Int, limit: Int, sortName: String?, sortDirection: String?): List<T> { val bt = SimpleBuilderTools() val rootNode = RSQLParser().parse(filter) val builder = em.criteriaBuilder val query = initQueryFunc<T, T>(builder, filter) val root = query.getRootByType(T::class.java) query.where(PredicateBuilderObj.createPredicate(rootNode, em, bt, root, query)) query.select(root) // 原数据库排序逻辑(当前不可用) if (sortName !== null) { val expression = root.get<Expression<*>>(sortName) query.orderBy(if (sortDirection == "ASC") builder.asc(expression) else builder.desc(expression)) } return em.createQuery(query).setFirstResult(offset * limit).setMaxResults(limit).resultList }
尝试的无效代码:
val prop = T::class.java.javaClass.kotlin.declaredMemberProperties.find { it.name == sortName } return em.createQuery(query).setFirstResult(offset * limit).setMaxResults(limit).resultList.sortedBy(prop)
问题分析
无效代码的核心错误是T::class.java.javaClass.kotlin——这里拿到的是Class类的KClass,而非泛型T的KClass,自然找不到目标属性。
解决方案
方案1:修正属性获取逻辑,使用sortedBy/sortedByDescending
直接通过T::class获取泛型类型的属性,转换为排序所需的函数式接口:
inline fun <reified T> findByFilterFunc(filter: String, em: EntityManager, offset: Int, limit: Int, sortName: String?, sortDirection: String?): List<T> { val bt = SimpleBuilderTools() val rootNode = RSQLParser().parse(filter) val builder = em.criteriaBuilder val query = initQueryFunc<T, T>(builder, filter) val root = query.getRootByType(T::class.java) query.where(PredicateBuilderObj.createPredicate(rootNode, em, bt, root, query)) query.select(root) // 执行查询获取结果 val resultList = em.createQuery(query).setFirstResult(offset * limit).setMaxResults(limit).resultList // 内存排序逻辑 if (sortName == null) return resultList // 获取目标属性(memberProperties包含继承属性,declaredMemberProperties仅包含当前类声明的属性) val prop = T::class.memberProperties.find { it.name == sortName } as? KProperty1<T, Comparable<*>> ?: return resultList // 属性不存在或不可比较时返回原列表 // 根据排序方向选择排序方式 return if (sortDirection == "ASC") { resultList.sortedBy { prop.get(it) } } else { resultList.sortedByDescending { prop.get(it) } } }
方案2:自定义Comparator实现排序
如果需要更灵活的排序逻辑(比如处理非可比较类型、空值优先级等),可以自定义Comparator:
inline fun <reified T> findByFilterFunc(filter: String, em: EntityManager, offset: Int, limit: Int, sortName: String?, sortDirection: String?): List<T> { // ... 原查询逻辑不变 ... val resultList = em.createQuery(query).setFirstResult(offset * limit).setMaxResults(limit).resultList if (sortName == null) return resultList val prop = T::class.memberProperties.find { it.name == sortName } ?: return resultList // 设置属性可访问(如果是private属性) if (!prop.isAccessible) { prop.isAccessible = true } // 自定义Comparator val comparator = Comparator<T> { a, b -> val valueA = prop.get(a) val valueB = prop.get(b) when { valueA == null && valueB == null -> 0 valueA == null -> -1 // 空值排在前面,可根据需求调整 valueB == null -> 1 valueA is Comparable<*> -> (valueA as Comparable<Any>).compareTo(valueB) else -> 0 // 非可比较类型不参与排序 } } // 根据排序方向反转比较器 val finalComparator = if (sortDirection == "DESC") comparator.reversed() else comparator return resultList.sortedWith(finalComparator) }
关键注意事项
- 属性访问权限:如果目标属性是private,需要调用
prop.isAccessible = true确保能读取值。 - 类型安全:如果属性值不是
Comparable类型,需要额外处理(比如转换为字符串比较),避免类型转换异常。 - 空值处理:根据业务需求定义空值的排序优先级(比如空值在前或在后)。
内容的提问来源于stack exchange,提问作者dtadescu
相关产品推荐
相关产品推荐

