Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reference constructor of type excluding optional parameters

I am having class of type A, which has contructor, that requires parameter x and has some optional parameters:

class A(x: String, y: String = "default_y")

Now I want to reference constructor with required parameters:

var function: (String) -> A = ::A

Now I am getting problem of incompatible types as signature of that constructor is 2 strings, not just one.

When I add this constructor overload, compiler stops complaining:

class A(x: String, y: String = "default_y") {

constructor(x: String): this(x, "default_y")
}
//added just so you can see full code
var function: (String) -> A = ::A

I am getting this bit of redundancy now. I can of course do something about it (extract "default_y" to constant or remove default parameter from primary constructor) to remove redundancy, but it is all just sugar code that doesn't really do anything. Just allows me to reference it without complaining. Is there way to reference constructor (and probably function as well) as functions with only required parameters?

like image 656
K.H. Avatar asked Dec 06 '25 02:12

K.H.


2 Answers

As stated here and also here, you cannot make use of default arguments via reflection.

The default value of a method parameter is an arbitrary expression which can only be represented as a chunk of bytecode; there is no other representation that can be used in reflection. Parameter Info retrieves default parameter values by parsing source code.

As a workaround, you could let the compiler generate the JVM overloads for your constructor and then use Java reflection to invoke the constructor that takes the single String argument:

class A @JvmOverloads constructor(x: String, val y: String = "default_y")

val con: Constructor<A> = A::class.java.constructors
        .filterIsInstance<Constructor<A>>()
        .find { it.parameterCount == 1 } ?: throw IllegalStateException("Not found!")
val newInstance: A = con.newInstance("myArg")
println(newInstance.y) // Prints 'default_y'

EDIT:

With callBy you can also invoke the constructor using Kotlin reflection:

val con = MyClass::class.constructors.first()
val newInst =
    con.callBy(mapOf(con.parameters.first() to "myArg"))
like image 84
s1m0nw1 Avatar answered Dec 09 '25 22:12

s1m0nw1


While there's no way to get a function reference with the default parameters removed from the signature, you can use a lambda instead of a function reference and call the constructor providing only the required argument:

val function: (String) -> A = { A(it) } // uses the default for `y`
like image 32
hotkey Avatar answered Dec 09 '25 22:12

hotkey