Coming Up for Air

Java to Kotlin Conversion Question. And Answer.

Thursday, July 18, 2019 |

Recently, in the #kotlin channel on Freenode, a user asked a question about what was happening to his Java code when using IDEA’s convert-to-Kotlin functionality. He left before anyone had the time to answer, and while he likely doesn’t read my blog, I’m going to answer his question here anyway. :)

Here is his (slightly edited) question:

I use intellij idea to convert a java code to kotlin, but there’s something I don’t understand, https://paste.ubuntu.com/p/NNbRwmR4mG/

I don’t know why kotlin convert the second parameter in memory.subscribeToEvent which is a object to a lambda?

and idea turn f(a,b) in java to f(a)(b) in kotlin

auto currying?

And, just in case the paste disappears, here are its contents:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
java:
tts = new ALTextToSpeech(session);
        frontTactilSubscriptionId = 0;

        // Subscribe to FrontTactilTouched event,
        // create an EventCallback expecting a Float.
        frontTactilSubscriptionId = memory.subscribeToEvent(
                "FrontTactilTouched", new EventCallback<Float>() {
                    @Override
                    public void onEvent(Float arg0)
                            throws InterruptedException, CallError {
                        // 1 means the sensor has been pressed
                        if (arg0 > 0) {
                            tts.say("ouch!");
                        }
                    }
                });

kotlin:
tts = ALTextToSpeech(session)
    frontTactilSubscriptionId = 0

    // Subscribe to FrontTactilTouched event,
    // create an EventCallback expecting a Float.
    frontTactilSubscriptionId = memory.subscribeToEvent(
        "FrontTactilTouched"
    ) { arg0 ->
        // 1 means the sensor has been pressed
        if (arg0 > 0) {
            tts.say("ouch!")
        }
    }

To understand why the Kotlin code looks the way it does, it’s important to understand a bit about the Java code. Without knowing anything about the method memory.subscribeToEvent, it seems clear that it takes a String (perhaps an event name?), and a callback which is, of course, called when the event happens. Incidentally, and not important here, it seems to return a subscription ID, which I assume one can use to cancel the subscription.

The interesting part of all of this is that second parameter, EventCallBack<T>. What the Java code is doing is creating an anonymous instance of the interface and passing it directly to the method. What’s interesting about the interface is that it is what is known as a Single Abstract Method interface, meaning it has…​ wait for it…​ only one abstract method. In Java, lambdas are actually implemented internally, if I recall correctly, as instances of SAMs, often done silently by the compiler. This code, then, could be written like this:

1
2
3
4
5
6
7
frontTactilSubscriptionId = memory.subscribeToEvent(
    "FrontTactilTouched",
    (Float arg0) -> {
        if (arg0 > 0) {
            tts.say("ouch!");
        }
    });

If memory serves, the compiler is smart enough to know that the method takes an EventCallback, and sees that the lambda requires a single Float, which happens to match the single abstract method of the interface, so this lambda-ized version of the code is magically converted to the non-lambda version above in the bytecode, and life moves on.

Now, when we move to Kotlin, the converter code is also smart enough to recognize the SAM in the original Java code, so it converts that to a lambda. It goes a step further, though, and makes the code more idiomatic: in Kotlin, if the last parameter to a method is a lambda, the compiler will allow you to specify that outside the parenthesis in your calling code. This code, then:

1
2
3
4
5
6
7
8
9
frontTactilSubscriptionId = memory.subscribeToEvent(
        "FrontTactilTouched",
        { arg0 ->
        // 1 means the sensor has been pressed
        if (arg0 > 0) {
            tts.say("ouch!")
        }
    )
}

is functionally equivalent to this:

1
2
3
4
5
6
frontTactilSubscriptionId = memory.subscribeToEvent("FrontTactilTouched") { arg0 ->
        // 1 means the sensor has been pressed
        if (arg0 > 0) {
            tts.say("ouch!")
        }
}

And there you go. That’s my take on what’s going with that conversion. I hope you find that helpful. And accurate. :) If I missed something, hit the comments below and let’s talk.

Search

    Quotes

    Sample quote

    Quote source

    About

    My name is Jason Lee. I am a software developer living in the middle of Oklahoma. I’ve been a professional developer since 1997, using a variety of languages, including Java, Javascript, PHP, Python, Delphi, and even a bit of C#. I currently work for Red Hat on the WildFly/EAP team, where, among other things, I maintain integrations for some MicroProfile specs, OpenTelemetry, Micrometer, Jakarta Faces, and Bean Validation. (Full resume here. LinkedIn profile)

    I am the president of the Oklahoma City JUG, and an occasional speaker at the JUG and a variety of technical conferences.

    On the personal side, I’m active in my church, and enjoy bass guitar, running, fishing, and a variety of martial arts. I’m also married to a beautiful woman, and have two boys, who, thankfully, look like their mother.

    My Links

    Publications