How @JvmOverloads work on Kotlin when using default arguments?

817 Views Asked by At

I want to understand how to use JvmOverloads annotation better and how to handle kotlin default arguments in java in a better way.

Imagine I have a function like this:

@JvmOverloads
@JvmStatic
fun testFunction(age: Int, name: String = "", level: Int = 0, class: String = "")

The kotlin bytecode generates:

   @JvmStatic
   @JvmOverloads
   public static final void testFunction(int age, @NotNull String name, int level) {
      testFunction$default(age, name, level, (String)null, 8, (Object)null);
   }

   @JvmStatic
   @JvmOverloads
   public static final void testFunction(int age, @NotNull String name) {
      testFunction$default(age, name, 0, (String)null, 12, (Object)null);
   }

   @JvmStatic
   @JvmOverloads
   public static final void testFunction(int age) {
      testFunction$default(age, (String)null, 0, (String)null, 14, (Object)null);
   }

Which means I can call

testFunction(0)
testFunction(0, "John")
testFunction(0, "John", 1)
testFunction(0, "John", 1, "Warrior")

But I CANNOT call a function like this in java code:

testFunction(0,1)

But in kotlin would be possible! Since name and class have default values.

Could I get some help? How does this work? Am I using it wrong?

2

There are 2 best solutions below

0
Jorn On BEST ANSWER

You are correct. @JvmOverloads only generates overloads for default parameter values from last to first. Not all possible combinations. It can't do that, because there would be conflicting signatures. For example, if there was a method with only your age parameter and only your level parameter, how would Java know which one to call?

You can tinker with this by changing the parameter order. So if you need a method with level and age parameters, but no name, you can put those two parameters first in the Kotlin function.

0
dominicoder On

testFunction(0,1)

But in kotlin would be possible!

Actually, it would not:

https://kotlinlang.org/docs/functions.html#named-arguments

You are also able to skip specific arguments with default values, rather than omitting them all. However, after the first skipped argument, you must name all subsequent arguments:

So you would have to do:

testFunction(0, level = 1)

Could I get some help?

With what?

How does this work?

You already showed that you used the @JvmOverloads and that you can see the bytecode generated. That's how it works. https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-overloads/

Am I using it wrong?

Not necessarily. But if your desire is to be able to do testFunction(0, 1) then you have to change the declared order of the parameters of the function:

@JvmOverloads
@JvmStatic
fun testFunction(age: Int, level: Int = 0, name: String = "", class: String = "")

Then your testFunction(0, 1) would work in both Java and Kotlin (without a named argument).