How Kotlin supports default function parameters when Java does not
You have probably already used Kotlin’s default function parameters to simplify function calls and encapsulate default behavior. However, Java does not provide this support. Yet, both languages are interoperable and Kotlin code even gets compiled to Java bytecode, when targeting Kotlin/JVM. This article helps you understand how Kotlin bridges the gap between supporting default parameters, yet remaining interoperable with Java. Let’s get going!
What is meant by interoperability?
In this article we discuss Kotlin’s interoperability with Java, that is, when Kotlin is compiled to target Kotlin/JVM. In a nutshell,
Kotlin and Java files can exist in the same project and can directly communicate with each other. You may call Java methods from Kotlin and Kotlin functions from Java. Similarly, you may use Java’s APIs and frameworks in Kotlin and vice-versa.
Default parameters’ (lack of) interoperability
Let’s take a look at what happens behind the scenes when we compile a Kotlin function with default parameters.
Decompiling the bytecode to Java, we get this (simplified for clarity) definition of sample
that does not support any default parameters:
Notice that the Kotlin version of sample
can be called in four different ways:
Yet, the more verbose Java version can only be called if all two arguments are explicitly passed. That is, when calling the Kotlin function from a Java file we’d get:
Default parameters’ (better) interoperability
To provide better interoperability with Java in more specific cases, JVM provides several annotations to instruct the compiler how to compile the Kotlin code. For example, the @JvmOverloads
annotation
instructs the Kotlin compiler to generate overloads for a given function that substitute default parameter values.
In practice, this means that by adding the @JvmOverloads
annotation to your Kotlin function, you ask the Kotlin compiler to try to reproduce the default parameter behavior by overloading the method in different ways. For example:
Now decompiles to the following method overloads. For simplicity we abstract Java’s implementation logic that uses synthetic methods.
In practice, this means that we can now call from Java, with no errors:
Limitations
Great, we learned that by using method overloading, Java is able to provide a better interoperability with Kotlin’s default parameter functions. But you might have noticed that there’s still one missing case in the example above. What if we would like to call sample
with its default a
parameter and a custom b
?
This is possible in Kotlin, since it supports named arguments, but it is not supported in Java. Since parameters a
and b
are both of type String
, method overloading is not able to differentiate between them, so we are not able to replicate the call above in Java.
Thanks for reading! If you like the content, don’t forget to clap and follow for more!