Search code examples
kotlingenericscriteria-api

Kotlin generics in Criteria Api


I'm trying to add dynamic predicate translating some domain objects into javax.persistence.criteria.Predicate, however I have problems with correctly specifying types in generic methods. Below is a simplified piece of code presenting more or less the method contract I have to uphold. By that I mean that both path and value are calculated somehow.

enum class Operator { EQUAL, NOT_EQUAL, GREATER_THAN_OR_EQUAL, LESS_THAN_OR_EQUAL, GREATER_THAN, LESS_THAN, }

inline fun <T> buildPredicate(
            root: Root<*>,
            builder: CriteriaBuilder,
            operator: Operator,
            pathSupplier: () -> Path<T>,
            valueSupplier: () -> T
        ) {
            val path = pathSupplier()
            val value = valueSupplier()
            when (operator) {
                EQUAL -> builder.equal(path, value)              // ok
                GREATER_THAN -> builder.greaterThan(path, value) // this wont compile though
                else -> throw IllegalArgumentException()
            }
        }

On the marked line it shows

None of the following functions can be called with the arguments supplied.

there is a definition I'm targeting though

<Y extends Comparable<? super Y>> Predicate greaterThan(Expression<? extends Y> x, Y y);

Is this even doable?

As a side note I may add that in runtime T from the example above will resolve to either some enum, String or LocalDateTime.


Solution

  • Supply the bounds for the method using the where T: Comparable<T> clause (see amended method below) - further info - https://kotlinlang.org/docs/generics.html#type-erasure;

    inline fun <T> buildPredicate(
        root: Root<*>,
        builder: CriteriaBuilder,
        operator: Operator,
        pathSupplier: () -> Path<T>,
        valueSupplier: () -> T
    ) where T: Comparable<T>{
        val path = pathSupplier()
        val value = valueSupplier()
        when (operator) {
            Operator.EQUAL -> builder.equal(path, value)              // ok
            Operator.GREATER_THAN -> builder.greaterThan(path, value) // this compiles
            else -> throw IllegalArgumentException()
        }
    }