You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 10:23:34