Hacker News new | ask | show | jobs
by tsvetkov 3146 days ago
> Java -> Kotlin is effectively one-way interop

Could you provide some more details? I'm a bit surprised by your statement because Kotlin team has put a lot of effort into providing mostly seamless two way interop. Kotlin project itself is ~50% Java (specifically to test interop).

3 comments

I'm really rusty, but if I can recall the simplest example correctly, you can't just naively go from this Kotlin code:

    Server { req -> Response().html("<h1>Hello world</h1>") }
to this Java code:

    new Server(req -> new Response().html("<h1>Hello world</h1>") 
Because Server is implemented in Kotlin and the Java lambda certainly isn't a kotlin.Function.

If that's a bad example (foggy memory), another one would be the use of @DslMarker or reflection to build my type-safe router: https://github.com/danneu/kog#type-safe-routing. Or how about inline functions with reified generics.

Kotlin does have Kotlin->Jvm interop annotations, but my take-away was that you would have to put some thought into your API and possibly make concessions so that you could sufficiently annotate it to the point that it's pleasant to use from Java. Not everything maps.

For example, you can see that OP went the route of using Java at the interface edges yet Kotlin internally to solve interop for their project.

I admit that "effectively one-way interop" is too heavy-handed of a statement, but there's definitely asymmetry that I ran into on my own project. For example, I would not recommend building something in Kotlin in the hopes that, once you're done, you can just sprinkle on some @Jvm annotations and end up with an API that's compelling to use from Java.

That's because the concept of a typed lambda function doesn't really exist in Java; in Java 8 lambdas are syntactic sugar for SAM-type object creation. (There are other interface warts too: @DslMarker and parameterized receivers, as you mention; treatment of void vs. Unit; different default collection types; destructuring data types, and the need to clutter your code with 'new' if you use them a lot; etc.)

It's similar to any interop problem: if you want your library to be accessible from lots of languages, its interface better be the lowest common denominator of the features they support.

There's nothing stopping you, though, from defining your interfaces in Java and then implementing those interfaces in Kotlin. The Kotlin interop code even will do a good job letting you use Kotlin's syntactic sugar for this, eg. you can pass Kotlin lambdas to functions that expect a SAM-type, or use 'with()' to treat a conventional builder pattern as a DSL.

Hasn't really been my experience - I've found it pretty easy to write eg. Hadoop classes, Rome plugins, servlets from Kotlin. You do need to be aware of what code Kotlin is generating for you, and there are occasional warts, but in general you just implement your interfaces and enjoy the boilerplate that Kotlin frees you from typing.
I don't have interop issues with your examples either. Calling Java from Kotlin is very pleasant.

What I found is that it's the other direction that takes some thought. For example, my Kotlin library needs more help than just @Jvm annotations to be usable from Java.

All my examples are calls into Kotlin from Java. With Hadoop, you're implementing interfaces that the framework instantiates via reflection and then the Java framework calls the Kotlin code. With Rome, you're creating Kotlin objects that implement a Java interface and then they're called from Java code. With servlets, the servlet container (Jetty, in this case) holds a reference to the servlet and then calls its serve() method upon an HTTP request.
You are surprised because it is not true. I can prove that you can write two-way interop just check the library I've written and posted above.
You can write two-way interop -- I didn't mean to suggest otherwise.

But there are Kotlin features that don't map like type-safe @Dsl's which puts you in a position where you have to make concessions for interop.

Now that is true. You also can't use reified generics (which is a bit hacky anyway).
"It totally works...except for A, B....oh yeah and C, but C kinda sucks....well D is awful too, you should never use D...."

So basically, he was right, and you weren't. There is no seamless 2-way interop, yes?

I don't understand this sentiment. I understand Kotlin has more features than Java, and of course it's tough to call the advanced things from Java. At least so I would think.
No he was not right. You can write two-way interop and it is not hard. I have actually written a library which does it. He did not say that "the two-way interop is not seamless". He said that you "can't write two way interop code" which is not true.