0

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?

CryptoFool
  • 21,719
  • 5
  • 26
  • 44
  • You may straight up _not be able to do this._ – Louis Wasserman Apr 28 '19 at 19:48
  • :( . I was fearful that that would be the case. - I think I'd be able to accept that more easily if not for the fact that providing a literal value for the parameter provides the behavior I want. I was a little surprised actually when I found that that worked. – CryptoFool Apr 28 '19 at 19:52
  • @LouisWasserman, Per yole's answer and my comment on it, you're still technically right. His solution is a trick really...not a full solution. It does explain why passing the literal "false" value works. – CryptoFool Apr 28 '19 at 20:03
  • @LouisWasserman, I'd suggest following the thread of comments on the provided answer. It's very enlightening. The fact that I can't even call the String versions of these methods from Java speaks volumes. The Kotlin code literally produces two methods with the same signature. Yikes! I'm very glad to have gotten this education. – CryptoFool Apr 28 '19 at 20:16

1 Answers1

2

A non-nullable Boolean is simply boolean in Java. To call the non-null overload, simply use a cast:

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

This only works because Kotlin optimizes the second method to accept a boolean rather than a Boolean, so the signatures of the two methods are different in the generated bytecode. In the case of equivalent methods accepting a String parameter, there's no way to call either of these methods from Java. Kotlin creates two methods with identical signatures, and when you attempt to call one of them from Java, you get an "Ambiguous method call" error.

CryptoFool
  • 21,719
  • 5
  • 26
  • 44
yole
  • 92,896
  • 20
  • 260
  • 197
  • Dude! That's cool! It doesn't, however, solve my problem in general. I didn't think to post the equivalent for a String property, which will come up for me. This works because in this case, the property type includes a primitive equivalent. Would there be a solution if this were a String property ... or a FooBar property for that matter? – CryptoFool Apr 28 '19 at 19:59
  • For a `String` property, there would be no way to resolve the overloads, because the parameter types would be identical from the point of view of Java. The Java class file format allows to define methods which have the same parameter types but different return types, so the Kotlin code compiles, but the Java language does not allow you to choose which of the overloads should be called (because there is no way to define this kind of overload in Java). – yole Apr 28 '19 at 20:05
  • So the parameter types for the two generated methods in this case are not the same? Kotlin literally defines the second overload to accept a primitive 'boolean' while the first accepts a 'Boolean'. It seems strange to me that it would make that change given that it can't do so in general. - seems like a great opportunity for me to do some kotlin -> java decompiling and see what is actually going on. Thanks for your help and in pointing me to do more research on my own. – CryptoFool Apr 28 '19 at 20:07
  • Correct. It's very important for Kotlin to make this change for efficiency purposes - otherwise, every time you passed a `Boolean` or `Int` parameter to a Kotlin method, it would be boxed. – yole Apr 28 '19 at 20:08
  • So it's an optimization then, that happens to solve my problem? – CryptoFool Apr 28 '19 at 20:09
  • Yes, you can put it that way. – yole Apr 28 '19 at 20:09
  • Don't you mean "passed a `boolean` or `int`"? – CryptoFool Apr 28 '19 at 20:10
  • You are absolutely right. Realized I should try what I'm asking regarding String. I get an error: "Ambiguous method call". You're brilliant! - I guess this means I need to rename my methods so the two flavors have unique names. – CryptoFool Apr 28 '19 at 20:14
  • (I think renaming one of the methods would probably be safer for humans as well as for compilers…) – gidds Apr 28 '19 at 21:39
  • Yeah @gidds, you're probably right. I'm new enough with starting to wedge Kotlin into my Java codebase that questions like this are going to be somewhat academic for a while. When I got the original code reviewed, quite possibly one of my engineers would have provided the same wisdom you have. – CryptoFool Apr 28 '19 at 22:37
  • @Steve: It's always fun to investigate, try things out, and push the boundaries to see what's possible.  That's how you learn!  And this is an interesting question.  — But I think it'd be covered by an item in Joshua Bloch's ‘Effective Java’, which recommends you “avoid confusing uses of overloading”.  (So it's his wisdom, not mine :-)  See: http://www.informit.com/articles/article.aspx?p=31551&seqNum=4 – gidds Apr 29 '19 at 11:09