1

I have two methods:

class Example {
    fun method(name: String): String {}

    fun method(name: String, length: Int): String {}
}

And at some point in my code I need to use the second version of method via reflection, like so:

val func = Example::method

This can't compile because the compiler can't tell the difference between the two definitions of this overloaded method. I can't find a way to specify the parameteres for the function. The documentation says:

Alternatively, you can provide the necessary context by storing the method reference in a variable with an explicitly specified type:

val predicate: (String) -> Boolean = ::isOdd // refers to isOdd(x: String)

But in my case, the function assigned to func can be arbitrary, it does not necessarily has two parameters of types String and Int, and in other cases the function assigned to this property only has the single String parameter. But in this case, I need the version with two parameters.

So, is there any way to specify which function I'm using, and if not, is there any workaround?

wouldnotliketo
  • 153
  • 1
  • 10
  • but how are you going to use `val func`? you have to call it either by `func("name")` or `func("name", 1)` at compile time. which means you need to know the function definition beforehand – sidgate Jul 24 '20 at 07:44
  • @sidgate That's a rational concern, and perhaps my way of addressing this is not the best. I am not calling it, however, I am using it for code generation with KotlinPoet. This way instead of storing function name, package and parameters separately to use those later (I need to generate a function call) I can store just one object. Later when I generate said call, I check function parameters (there are several similar functions that all have a `length: Int` parameter, for example) and handle those accordingly. – wouldnotliketo Jul 24 '20 at 08:32
  • 1
    may be you can try reflection methods like `memberFunctions` or java style `getMethod` – sidgate Jul 24 '20 at 08:37

1 Answers1

0

I think the problem with reflection is that a function reference always refers to one particular function. So in your case you would need 2 function references to match both versions of method:

val e = Example()
val func1: (String) -> String      = e::method 
val func2: (String, Int) -> String = e::method

And this would most certainly not meet the need why you overloaded the function since you either have to use func1 or func2 respectively

Another approach would be to use a function with default parameter(s) instead of overloading. In addition I declared the original functions private and use a public wrapper instead to have both functions still separated:

(for sure you can also define just one method function with optional parameter as well and differentiate within this function):

class Example
{
    private fun method(name: String): String {
       return "Method1: $name"
    }

    private fun method(name: String, length: Int): String {
       return "Method2: $name ($length)"
    }

    fun method(name: String, length: Int? = null) : String {
        if (length == null)
           return method(name)
        else
           return method(name, length)
    }
} 

fun main(args: Array<String>) {
    val e = Example()
    
    // local function because lambda expression cannot have default parameters
    fun func (name: String, length: Int? = null): String {
        return e.method(name, length)
    }
    
    println(func("John"))
    println(func("Doe", 42))
}  
Odysseus
  • 1,213
  • 4
  • 12