How do I force a Java call to overloaded Kotlin method accepting a non-nulllable parameter?

#1

In Kotlin, I have two overloaded methods defined on an object. They have the same signature except that in one case, a parameter is nullable and in the other case, that same parameter is not. Based on if the passed in parameter is nullable or not, one overload is chosen over the other:

fun getBooleanPropertyValue(key: String, dflt: Boolean? = null) : Optional<Boolean> {
}

fun getBooleanPropertyValue(key: String, dflt: Boolean) : Boolean {
}

I hope it is clear what is going on here. If I can supply a non-nullable default value for a property, then the class can always return a non-null value for that property, and so it returns it directly. If I can’t supply a non-nullable default, then the class returns an Optional because it can’t be sure it can pass back a non-null object.

This all works great in Kotlin, but now I want to call the non-nullable version from Java. I have something like this:

static final Boolean defaultIsDisabled = false;
Boolean isDisabled = propSource.getBooleanPropertyValue("isDisabled", defaultIsDisabled);

I get an error here because Java calls the method that accepts a non-nullable default, and so therefore returns an Optional, and I’m trying to assign that to a Boolean. But in this case, the default I’m passing can never be null and I want to get back a Boolean directly.

I know Java can call the other method because this works:

Boolean isDisabled = propSource.getBooleanPropertyValue("isDisabled", false);

I’ve tried using various flavors of the @Notnull/@Nonnull annotations on the defaultIsDisabled value to tell Java it can never be null, but I can’t figure out how to get Java to call the version of the method that returns a bare Boolean.

Can anyone tell me how to get Java to call the version of this method that accepts a non-nullable default value while supplying that value via a variable?

#2

When working with primitives, the Kotlin compiler will translate a non-nullable primitive to the actual primitive (boolean) while the nullable primitive will be translated to a boxed version (Boolean). As you use a boxed value in Java, you should expect the nullable version (that returns an optional) to be called.

#3

Thanks pdvrieze for your answer. Since this post got put on hold here, I got an answer on Stack Overflow. It was the same answer.

But then I asked…what if the parameter type were a String or some other object that didn’t have a primitive type equivalent? The answer to that question was very interesting. I was told that Kotlin would then produce two methods in the bytecode with the same signature, and Java wouldn’t know what to do with that. Sure enough, I tried it in my code, and if I try to call the String version of this method (pair) in Java, I get a compile time error: “Ambiguous method call”. I always like to have a complete understanding of things, and this revelation along with the first answer let me fully understand.

For reference, here’s the S.O. question: https://stackoverflow.com/questions/55893584/how-do-i-force-a-java-call-to-overloaded-kotlin-method-accepting-a-non-nulllable

#4

For the latter difference, the problem is here that for the JVM the return type of a function is part of its signature (if you look at the class file, covariant return types causes synthetic proxies to be created). But the Java languages does not allow the distinction (neither does Kotlin except where the parameter types are different to Kotlin, but not the JVM - ie different nullability). The “solution” is to use @JvmName to specify a different name that Java can use to distinguish. Alternatively, you can mark one of the methods as @JvmSynthetic so that it will not be considered (visible for user code) by the Java compiler.

#5

Thanks! This is great feedback. I did understand that there was something that could be done with annotations, but that seems like code smell to me if there’s another way to solve this. I don’t want to muck up my Kotlin for the sake of Java if I can avoid it.

In this case, there was no reason to not rename one set of methods. I renamed one set so I now have ‘getOptionalXXXPropertyValue’ and ‘getXXXPropertyValue’ methods, the former returning an Optional and the latter returning the type itself. In fact, I saw this as the thing to do almost before asking the question. The question was more because I want to understand these nuances. You’ve done a great job of educating me. I can see all of this in my head now in terms of what’s in the Kotlin bytecode, and what Java is doing to cope with it.

Thanks again!