Don’t argue with default arguments
Kotlin Vocabulary
Short and easy to use, default arguments allow you to implement function overloads without the boilerplate. Like many Kotlin features, this can feel like magic. Are you curious to learn its secrets? Read on to find out how default arguments work under the hood.
Basic usage
If you need to overload a function, instead of implementing the same function multiple times, you can use default arguments:
Default arguments can be applied to constructors as well:
Java interop
By default, Java doesn’t recognize the default value overload:
To instruct the compiler to generate the overload methods, use the @JvmOverloads
annotation on your Kotlin function:
Under the hood
Let’s look at the Java decompiled code to see what the compiler generates for us: Tools -> Kotlin -> Show Kotlin Bytecode
then press the Decompile
button.
Functions
We see that the compiler generates 2 functions:
play
— that has 1 parameter:Toy
and is called when default arguments are not used- a synthetic method
play$default
— that has 3 parameters:Toy
, anint
and anObject
; it’s called whenever the default arguments are used. TheObject
parameter is alwaysnull
but the value of the int differs. Let’s see how!
The int parameter
The value of the int parameter of play$default
is computed based on the number and the index of the arguments that have a default argument passed in. Based on the value of this parameter, the Kotlin compiler knows with which parameters to call the play function.
In our play()
example call, the argument at index 0 is using the default argument. Therefore, play$default
is called with int var1 = 2⁰
:
play$default((Toy)null, 1, (Object)null);
The play$default
implementation then knows that the value of var0
should be replaced with the default value.
Let’s take a more complex example to see how the int parameter behaves. Let’s expand our play
function and, at the call site, use the default argument for doggo
and toy
:
Let’s see what happened in the decompiled code:
We see that now, our int parameter is 5. Here’s how this was computed: the parameters at positions 0 and 2 are using the default argument so var3 = 2⁰ + 2² = 5
. In a bitwise &
operation, the parameters get evaluated as such:
var3 & 1 != 0
istrue
sovar0 = goodDoggo
var3 & 2 != 0
isfalse
sovar1
is not replacedvar3 & 4 != 0
istrue
sovar2 = SqueakyToy
Based on the bitmask applied to var3
, the compiler can compute which parameters should be replaced with default values.
The Object parameter
In the examples above you might have noticed that the value of the Object
parameter is always null and that it’s actually never used in the play$default
function. This parameter is connected to supporting default values in overriding functions.
Default arguments and Inheritance
What happens when we want to override a function with default arguments?
Let’s change the example above and:
- Make
play
anopen
function ofDoggo
andDoggo
anopen
class. - Create a new
PlayfulDoggo
class, that extendsDoggo
and overridesplay
When we want to set a default value in PlayfulDoggo.play we see that we are not allowed: An overriding function is not allowed to specify default values for its parameters
If we remove the override
and we check the decompiled code, PlayfulDoggo.play()
looks like this:
Does this mean that super calls with default arguments will be supported in the future? We’ll just have to wait and see.
Constructors
For constructors, the decompiled Java code has only one difference. Let’s take a look:
Constructors also create a synthetic method but, instead of the Object
used in functions, constructors use a DefaultConstructorMarker
which is called with null
:
Secondary constructors with default arguments will also generate another synthetic method using a DefaultConstructorMarker
, like primary constructors:
Conclusion
Simple and sweet, default arguments decrease the amount of boilerplate code we need to write when dealing with overloaded methods, allowing us to set default values for our parameters. As is the case for a lot of Kotlin keywords, when can understand their magic by peeking at the code it writes for us. Check out our other Kotlin Vocabulary posts for more.