Interoperability between Java and Kotlin is great, but the difference in how nullability is treated can sometimes pose interesting problems. This article tackles one particular problem and introduces some Kotlin concepts in the process.

Calling an overloaded Java method from Kotlin

Given a Java class called CakeBaker with two overloaded methods.

class CakeBaker {
    static Cake bakeCake(Recipe recipe) {...}
    static Cake bakeCake(String name) {...}
}

Both methods result in a cake, but the first accepts a recipe and the second simply accepts the name and produces the cake in some mystical way.

When importing this Java class into your Kotlin code and calling CakeBaker.bakeCake(..) with either a Recipe or a String, everything works as expected and life is great.

Calling an overloaded Java method from Kotlin with null as argument

Something peculiar happens when you don’t have a recipe or name to pass as an argument and you still need to call the method. Perhaps it has some functional meaning, or perhaps you want to write a test to validate that your Kotlin application knows how to deal with the implicit nullability of the Java method. You pass null as input into the method:

CakeBaker.bakeCake(null)

This should be valid, as Java objects are implicitly nullable, right? Wrong. The cake is a lie. The Kotlin compiler throws an error:

Overload resolution ambiguity between candidates:
fun bakeCake(recipe: Recipe!): (Mutable)Cake!
fun bakeCake(name: String!): (Mutable)Cake!

What’s going on? Because the Java objecttypes of both overloaded methods are nullable, the compiler doesn’t know which overloaded method you’re trying to call. That makes sense.

Were you to call this method from Java, you’d get a similar error:

error: reference to bakeCake is ambiguous
    bakeCake(null);
    ^
  both method bakeCake(String) in CakeBaker and method bakeCake(Recipe) in CakeBaker match

Calling a method from Kotlin using Named Arguments

You might think: "This is Kotlin, so I can use Named Arguments to tell the compiler which of the two overloaded methods to use and be done with it!"

CakeBaker.bakeCake(name = null)

Unfortunately, this will also not compile:

Named arguments are prohibited for non-Kotlin functions.

Calling a method from Kotlin with null cast to a specific type

The only remedy is to tell Kotlin explicitly which of the two nullable types you are nullifying, by Casting it.

One way is to use

CakeBaker.bakeCake(null as? String)

This compiles and runs, but something strange may happen here. IntelliJ will gray out the as? String bit, because it probably figures that as? after null will result in null, making the rest redundant. But when you remove it as suggested, you’ll be met with the overload ambiguity compiler error we were trying to solve.

What does work without warning is this.

CakeBaker.bakeCake(null as String?)

Now Kotlin will understand the null you’re passing it was intended as a String?, which translates to the Java String. Mind the questionmark postfix in Kotlin to indicate a nullable type; a plain String in Kotlin cannot be null after all.

In Java, you’d similarly cast null to one of the arguments:

CakeBaker.bakeCake((String) null);

It’s not pretty, and if you had the option you’d probably want to add a method to the Java library to avoid this situation entirely:

Cake CakeBaker.bakeCake() {
    return bakeCake((String) null);
}

But you don’t always have that option when using others' external libraries.

Calling a method from Kotlin via an extension method

Kotlin does offer another thing that can help avoid having to cast null every time you need to call the the method. For convenience, you could create an Extension Function in your calling Kotlin code:

fun CakeBaker.bakeCake() = bakeCake(null as String?)

The original Java class stays untouched, yet you can now call the CakeBaker.bakeCake() method for the same effect.

shadow-left