Can you use Kotlin libraries in Java without the Kotlin runtime?

If I have some library I’ve created in Kotlin, which exposes some functionality like:

class BookService {
  fun findBook(id: Number, title: String)

in order to use this API from a Java application, it would have to know how to construct Number and String, which means they would have to import the Kotlin stdlib in their Java application in order to work with an API created in Kotlin (assuming the interface uses Kotlin types). I know some types map directly to known JVM types, but many do not.

This seems onerous for devs who want to stay in java without directly depending on/importing Kotlin specific resources, and is actually one of the concerns I have heard often when I try to suggest Kotlin adoption to teammates.

Is this just a limitation that has to be accepted?

Well, yes and no.

Many Kotlin features are provided by the compiler and they don’t require any libs on runtime. That includes: data classes, properties, null safety (? Not sure about this, it may use e.g. Intrinsics in some cases), smart casts, type inference, inline functions, extensions, many syntactic sugars, etc. Other features are entirely provided by stdlib, for example collection transformations. Some functions from stdlib are inlined, so they can be used without stdlib (scoping functions), but not all such functions are fine, because they may reference other components of stdlib. On the other hand, not all features that seem to be related to the compiler will work, for example lambdas reference types inside stdlib.

So I guess it would be pretty painful to work on Kotlin library without stdlib. But technically it is possible. Just make stdlib compileOnly and add -Xno-param-assertions compiler option. It should work, at least to some degree.

Anyway, why is it such a big deal? I guess you already include several libraries to your project, maybe even tens of them. Why kotlin stdlib is different?

I think the concern is generally that multiple libraries that use different version of the kotlin runtime may have a requirement on a specific version, such that you need to shadow into a fat jar. Basically similar challenges that we’ve seen with hadoop and guava libs cause some of my colleagues to be hesitant of requiring kotlin stdlib for all java applications and libraries, and I’m trying to understand those concerns and whether some of them have workarounds (in this question, whether clients can use a kotlin lib without including the kotlin runtime themselves, which seems to be ‘only if you don’t have stdlib types in your exported interfaces’)

So far I never heard about such problems - they should occur in pure Kotlin projects as well. But I understand your concern, it is a mess when too many libs have the same dependency.

In both posts you said something about “Kotlin types”. What types do you mean? Stdlib is mostly about utility functions and extensions, I believe it doesn’t define too many types that aren’t mapped to Java types at compile time. Lack of ranges may be problematic as Kotlin doesn’t have a regular for loop (actually, iterating over ranges is compiled to a plain for-loop, so it works without stdlib). Function types will definitely be a problem. But generally, I think types aren’t the main problem. Lack of utils is.

My personal opinion is that I would probably stick to fully working Java than to crippled Kotlin. Developing without stdlib sounds pretty annoying, we would have to ask ourselves “Can I use this feature?” over and over again on a daily basis. And if there is a new member to a team, they have to learn this as well. I think it is better to either take a risk of including stdlib considering that stdlib keeps backwards compatibility and there are no problems with it so far. Or ignore Kotlin entirely and stick to Java.

For me, your problem is a more general problem of “Bill Of Materials”. Dependency convergence is a difficult problem, without perfect solution.

Note that if you aim to include more and more Kotlin in your codebase, at some point you will be forced to use the stdlib, because any tool using Kotlin (like Jackson extension for Kotlin, or Spring, etc.) will require the std-lib.

That’s why, for example, Spring-Boot provides a BOM for its dependencies. It allows them to define a set of compatible versions for all their dependencies, and each Spring-boot project use that as a starting point to resolve dependencies (and that’s why, in a Spring-boot project, there’ a lot of libraries that you can add as a dependency without specifying any version: it is infered from an imported BOM).