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.?
When you introspect the shape of your class using javap
you 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 get
and set
) are accessed as properties in Kotlin (documentation). This is the case for the getKeys()
, getSize()
and getMap()
methods you see in your javap
analysis.
So if you try y.keys
, y.size
or y.map
in Kotlin you should get the results you expect.
In fact, there is a further step going on here. I note that the class com.beust.klaxon.JsonObject
is written in Kotlin (see GitHub). You can see in the source it has a map
property, and it inherits keys
and size
from the MutableMap
interface 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).