I'm having trouble understanding why some public functions are apparently not available, although at least one other function is. The example I have is from Klaxon, but I suspect there's some mismatch between what I'm expecting and how Kotlin actually works, and it's not specific to Klaxon.
I am working with the command line interpreter on a Ubuntu 16.04 system. $j is the path to a jar file containing Klaxon and a bunch of other stuff. As you can see, I am working with kotlin 1.9.20. The file foo.json contains just { "foo" : 1234, "bar": null, "baz": [11, 22, 33, 44] }.
$ kotlin -cp $j
Welcome to Kotlin version 1.9.20 (JRE 17.0.8+9-LTS-211)
Type :help for help, :quit for quit
>>> val x = com.beust.klaxon.Parser.default ().parse ("foo.json")
>>> val y = x as com.beust.klaxon.JsonObject
>>> y.get ("baz")
res2: kotlin.Any = JsonArray(value=[11, 22, 33, 44])
So far, so good; that's just what I was hoping to see. Now try some other getSomething functions.
>>> y.getKeys ()
error: unresolved reference: getKeys
y.getKeys ()
^
>>> y.getSize ()
error: unresolved reference: getSize
y.getSize ()
^
>>> y.getMap ()
error: unresolved reference: getMap
y.getMap ()
^
I don' understand -- I have an object of the expected class,
>>> y::class
res6: kotlin.reflect.KClass<out com.beust.klaxon.JsonObject> = class com.beust.klaxon.JsonObject (Kotlin reflection is not available)
>>>
and the methods I named are all present in the class from what I can tell:
$ javap -cp $j com.beust.klaxon.JsonObject | grep get
public final java.util.Map<java.lang.String, java.lang.Object> getMap();
public java.util.Set<java.util.Map$Entry<java.lang.String, java.lang.Object>> getEntries();
public java.util.Set<java.lang.String> getKeys();
public int getSize();
public java.util.Collection<java.lang.Object> getValues();
public java.lang.Object get(java.lang.String);
public final java.lang.Object get(java.lang.Object);
Can someone explain what I'm seeing here? How should I call getKeys, getMap, etc.?
Java getters and setters are accessed as properties in Kotlin
When you introspect the shape of your class using
javapyou are looking at the Java representation of the class. Thus we need to think about the interaction of Kotlin and Java.Kotlin is fully interoperable with Java, but sometimes you call a method or access a property in a different way in Kotlin.
In particular, methods that follow the Java getter and setter conventions (ie those that start with
getandset) are accessed as properties in Kotlin (documentation). This is the case for thegetKeys(),getSize()andgetMap()methods you see in yourjavapanalysis.So if you try
y.keys,y.sizeory.mapin Kotlin you should get the results you expect.Note the original source was Kotlin
In fact, there is a further step going on here. I note that the class
com.beust.klaxon.JsonObjectis written in Kotlin (see GitHub). You can see in the source it has amapproperty, and it inheritskeysandsizefrom theMutableMapinterface that it implements. This source has been compiled to JVM bytecode by representing those Kotlin properties as Java getters and setters, following another published rule.This is the reverse of the rule noted above used to call Java code from Kotlin, which we would expect; otherwise there would be unexpected results when using compiled Kotlin libraries from Kotlin (as you are).