Home > Net >  How to resolve ambiguous Kotlin method reference
How to resolve ambiguous Kotlin method reference

Time:02-25

I define an enum that takes a method reference:

enum class Op(val param: (Expression<String>, String) -> Predicate) {
    GREATER_THAN(CriteriaBuilder::greaterThan)
}

There are two candidates in the CriteriaBuilder class, and the compiler can't figure out which one I mean ("Overload resolution ambiguity. All these functions match."):

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

Why can't Kotlin infer the right type? Clearly the first method is not possible. I can't figure out how to guide the compiler, casting it to the right type doesn't seem to work:

GREATER_THAN(CriteriaBuilder::greaterThan as (Expression<String>, String) -> Predicate)

Compiler complains "Cannot choose among the following candidates without completing type inference".

CodePudding user response:

Given the Javadoc of CriteriaBuilder, the method you're referring to is not static.

Since you're not providing an instance of CriteriaBuilder in your function reference, this means that CriteriaBuilder::greaterThan actually expects a receiver or parameter of type CriteriaBuilder in addition to the 2 arguments, which doesn't match the signature that you're expecting: (Expression<String>, String) -> Predicate.

If you want the caller of Op.param to provide the criteria builder instance, then I suggest to change Op.param's type to (CriteriaBuilder, Expression<String>, String) -> Predicate or CriteriaBuilder.(Expression<String>, String) -> Predicate - which would match the function reference CriteriaBuilder::greaterThan. The choice of which one to use depends on how you want to call it:

enum class Op(val param: CriteriaBuilder.(Expression<String>, String) -> Predicate) {
    GREATER_THAN(CriteriaBuilder::greaterThan)
}
// or

enum class Op(val param: (CriteriaBuilder, Expression<String>, String) -> Predicate) {
    GREATER_THAN(CriteriaBuilder::greaterThan)
}

If you want to have the CriteriaBuilder "built-in" the enum, you can instantiate it up front, and then use your specific instance in the function reference:

val yourCriteriaBuilder: CriteriaBuilder = TODO("create it somehow")

enum class Op(val param: (Expression<String>, String) -> Predicate) {
    GREATER_THAN(yourCriteriaBuilder::greaterThan)
}

I'm not familiar with the CriteriaBuilder API, so I'm not sure if this last option is actually desirable. You might want new instances each time instead - your call.

  • Related