{{announcement.body}}
{{announcement.title}}

Java-Friendly Kotlin: Default Arguments

DZone 's Guide to

Java-Friendly Kotlin: Default Arguments

Kotlin functions and constructors can define default arguments, allowing calls to them to skip any argument that has a default value.

· Java Zone ·
Free Resource

Kotlin functions and constructors can define default arguments, allowing calls to them to skip any argument that has a default value.

This allows the following function to be called in a number of ways:

Kotlin


More information can be found in the Kotlin documentation.

For the rest of this post, we will look at how you can include default arguments in your API while still providing excellent Java compatibility.

Trying to Call an Unfriendly Kotlin Function

You are not going to have a good time calling a function or constructor with default arguments from Java. The only way to call the function shown previously from Java is to provide every argument that it asks for:

Java


Without some help, there is no way for Java to understand the concept of default arguments.

Applying the @JvmOverloads Annotation

The @JvmOverloads annotation can be applied to functions and constructors to tell the compiler to generate extra overloads depending on their default arguments. The overloads are placed in the compiled byte code. You can then execute them from Java as you would with any other function or constructor.

Let’s have a look at the KDoc for @JvmOverloads which has a precise declaration of how it generates overloads:

Kotlin


In other words:

  • Starts at the last argument.
  • If the argument has a default value, an overload is generated without that argument, who uses the default value in its implementation.
  • If the penultimate argument has a default value, an overload without the last 2 arguments is generated.
  • Continues to apply this logic until it reaches the first argument or hits an argument that does not have a default value.

Some examples should help grasp this definition.

Below are the different ways to call the doStuff function from earlier in Java:

Kotlin
Java


There are now four options for Java to trigger (instead of 1 without the annotation). All the arguments have been removed, one by one, leaving 3 overloads where 1 requires no inputs at all.

It we remove one of the default values, then the generated options will change:

Kotlin
Java


Now, when creating overloads, the compiler hits b (second argument) and stops. It does not matter that a has a default value, the compiler will not progress from this point. Therefore, only a single extra overload is available to Java.

Applying the annotation to a class’ constructor requires a slight manipulation to its structure:

Kotlin


The annotation must be applied directly to one of the class’ constructor. In this case, as there is only a single constructor, the constructor keyword must be added (this can typically be omitted in a Kotlin class). Applying the annotation generates overloads in the same way that it does to a function.

The Annotation Isn’t Perfect

The issue with the @JvmOverloads annotation is that it does not generate every possible combination of arguments. Kotlin can bypass this as it has access to named arguments.

Java does not have this ability. Even with the @JvmOverloads annotation, this is what prevents Java from accessing all the same options as Kotlin.

It makes sense when you think about it.

A function or constructor can only be overloaded when it has different arguments from all other versions, for example (written in Kotlin but Java follows the same rules):

Kotlin


The difference between each overload is clear because the argument types are different. But if they were all the same type it becomes harder to use:

Kotlin


Named arguments makes this bearable (assuming they are named better than a, b and c). Allowing you to omit some arguments due to their default values.

Without the use of named arguments, the is no way for the compiler to distinguish between the objects that you pass to this function. So all it can do is pass them in, one by one, in order, into the function. This is the problem that @JvmOverloads faces. It cannot generate all possible iterations because there is not enough information to differentiate the overloads from each other. If all the arguments have different types, then technically the compiler can do it, but having a rule that is loosely applied would become confusing.

This is the reason that @JvmOverloads only generates overloads starting from the last argument, who must also have a default value, and then moves onto the next (backwards towards the first argument).

Cooperating With the Annotation

You can work around the limitations of @JmvOverloads. Doing so requires you to think about how your functions will be used and what combinations of parameters will be frequently used.

Below are some points for you to consider when writing a function annotated with @JvmOverloads:

  1. Order the arguments in order of importance, the first as the most important and decreasing as they go on.

    Kotlin
  2. Do not mix arguments with default values with ones that do not (easier to understand with the example).

    Kotlin
  3. Manually create overloads if there are still combinations missing that you deem useful.

    Kotlin

The main piece of advice I want to give you here is to really think about the functions you create. When creating a public API as part of a library, especially when you want it to play well with Java, spending time considering how your functions will be leveraged will make everyone happier.

Developers consuming a Kotlin API when using the language themselves can use its features to get around potential problems in your code. Java, on the other hand, requires you to apply some of your brainpower to write functions that are friendly to use.

Summary

Adding the @JvmOverloads annotation to your functions and constructors persuades them to play nicer with Java. It does so by generating additional overloads and placing them in the bytecode that Java interacts with. Adding the annotation does not always make your API easily accessible to Java, in those situations, it is on you to put the work in and craft a well thought out API.


If you enjoyed this post or found it helpful (or both) then please feel free to follow me on Twitter at @LankyDanDev and remember to share with anyone else who might find this useful!

Topics:
DEFAULT ARGUMENT, TUTORIAL, java, kotlin

Published at DZone with permission of Dan Newton , DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}