How kotlin is interoperable with java

Preview image for article

Hey people! Welcome to my blog on Kotlin interoperability with java.

1. How kotlin is interoperable with java

Kotlin is interoperable with the Java programming language because, much like the Java compiler, the Kotlin compiler produces JVM bytecode. This means Kotlin code can run on the Java Virtual Machine (JVM), right alongside Java classes.

In practice, this enables you to:

  • Call Kotlin functions and properties from your Java files without extra setup.
  • Call Java methods and fields from Kotlin just as if they were Kotlin methods.

Because both languages ultimately compile to .class files, the JVM can load them transparently, no matter which language they originated from.

Real-World Implications

  • You can incrementally adopt Kotlin in a Java project, rewriting classes as needed rather than all at once.
  • Mixed code is commonplace in Android development, letting you maintain existing Java codebases while taking advantage of Kotlin’s concise syntax, null-safety, and coroutines in new modules or features.

Now let’s get bit technical with picture below which clearly outlines what we need to understand. It outlines how both Kotlin and Java source code are compiled and packaged into a single application.

Kotlin and java interoperability
Featured image

2. How code get’s compiled

Keep referring to image above and step numbers to the bottom of the image.
We have both kotlin and java comparing along top and bottom.

Step 1 : Source Code
Both Hello.kt and Hello.java files have source code which needs to get compiled. So let’s just say we have hello world functions as our source code in those files.

Kotlin :

fun main() {
    println("Hello Kotlin")
}

Java :

public class Hello {

    public static void main(String[] args) {
        System.out.println("Hello Java");
    }
}

Now let’s compile this code.

Step 2: Compilation

  • Kotlin has its own compiler, often invoked via Gradle (kotlinc) in Android Studio or IntelliJ.
  • Java code is compiled by javac.
  • After compilation, both Hello.kt and Hello.java turn into .class files containing JVM bytecode.
    • For Kotlin, Hello.kt typically compiles to HelloKt.class.
    • For Java, Hello.java compiles to Hello.class.

Note: Kotlin code also depends on the Kotlin Standard Library (and possibly the Kotlin runtime). These libraries provide Kotlin-specific features (e.g., coroutines, extension functions, etc.) that Java doesn’t natively have.

Step 3: Packaging into a JAR

  • All .class files (from both Kotlin and Java) are typically packaged into a single .jar (Java Archive) or .aar (Android Archive) in Android projects.
  • A .jar includes a Java-specific manifest and is built on the ZIP format with a .jar extension.
  • This packaging step bundles the compiled bytecode into one distributable file, allowing cross-platform compatibility wherever the JVM is available.

Step 4: Execution on the JVM

  • The Java Virtual Machine loads the .class files from the JAR and translates the bytecode into platform-specific instructions at runtime (Just-In-Time compilation or interpretation).
  • Kotlin Runtime Library: If your code references Kotlin-specific features (like coroutines, data classes, extension functions), the Kotlin stdlib and any associated libraries are also loaded so these features can run seamlessly on the JVM.

3. Which platforms can we target

Since Kotlin (like Java) compiles to JVM bytecode, you can run it on any platform that supports a JVM, including desktop, server, and especially Android.

Beyond the JVM, Kotlin has multiplatform capabilities:

  • Android/iOS: Use Kotlin Multiplatform Mobile (KMM) to share common code between mobile platforms.
  • JavaScript: Compile Kotlin to JavaScript (Kotlin/JS) for web applications.
  • Native: Compile Kotlin into native binaries (Kotlin/Native) for platforms like macOS, iOS, Linux, and Windows without a JVM.
  • Data Science: Kotlin libraries such as Kotlin for Data Science let you leverage notebooks and numeric frameworks.

Key Point:

  • With Kotlin Multiplatform, you can write shared logic in Kotlin and produce platform-specific artifacts for a variety of environments.
  • On the JVM (which is the focus of basic interoperability), Kotlin and Java code can coexist flawlessly.

4. Java to kotlin converter

JetBrains’ IntelliJ IDEA and Android Studio include a built-in Java to Kotlin converter. This tool allows you to quickly transform a .java file into a .kt file, preserving the class’s logic but rewriting it in Kotlin syntax.
You can do this from refactor tools choose “Convert Java File to Kotlin File”

Example Conversion

Original Java file:

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello");
    }
}

Converted Kotlin :

import kotlin.jvm.JvmStatic

object Hello {
    @JvmStatic
    fun main(args: Array<String>) {
        println("Hello")
    }
}

Observations:

  • The converter might wrap your code in a Kotlin object if it detects a main method, making it easier to call from Java with Hello.main(args).
  • You’ll see additional annotations like @JvmStatic to facilitate static method calls from Java.

Note: The converter does an excellent job for most simple classes but might produce less-than-idiomatic Kotlin code if you rely heavily on Java-specific patterns (e.g., checked exceptions, certain reflection patterns). You can refine the converted code manually to leverage Kotlin’s data classes, extension functions, or coroutines.

5. How byte code looks and loads

Once compiled by either kotlinc or javac, Kotlin and Java produce .class files with JVM bytecode. Bytecode is a low-level, human-unfriendly representation of your logic. The JVM reads and executes it, but developers typically don’t manipulate .class files directly.

You can use IDE tools (e.g., IntelliJ’s “Show Kotlin Bytecode”) to see what your Kotlin or Java code looks like at the bytecode level.
But here’s how a function which prints a string will look like.

Image from ide which shows byte code

Execution and Loading

  • During runtime, the JVM loads these .class files into memory, verifying them for security (the Java “bytecode verifier”), and then just-in-time compiles them to native instructions.
  • If your code relies on Kotlin-specific language features, the Kotlin runtime and stdlib are also loaded so that classes like kotlin.String or the kotlin.collections package can be used alongside standard Java types.

You can try this yourself from your IDE.

Show kotlin bytecode

6. References

Kotlin multiplatform
Kotlin programming language
Kotlin interoperability documentation

Author

Reviewer & Editor

Similar Posts

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.