Skip to main content

Kotlin Compiles to JVM Bytecode and Interoperates With Java

Kotlin is not a replacement for the Java platform. It is a second language for it. When you run the Kotlin compiler on a JVM target, the output is ordinary Java bytecode in ordinary class files. The JVM cannot tell the difference, the JIT compiler optimizes it the same way, and every Java library on your classpath is available exactly as it is. That single design decision is the reason Kotlin could spread through large Java codebases without a rewrite.

The interoperability is deliberate and deeper than bytecode compatibility. Kotlin's own standard library for the JVM depends on the Java Class Library, so the two share collections, threads, IO, and reflection instead of maintaining parallel worlds. Kotlin also smooths over differences in the other direction, so Java code can call Kotlin code without awkward shims. Top level functions and properties, which Kotlin permits outside any class, are compiled into a class file named after the file, and you can rename that class explicitly with a file level annotation such as @file:JvmName("Utils"). Static access is available through annotations like @JvmStatic and @JvmField, and Kotlin properties are exposed to Java as conventional getter and setter methods.

That two way bridge is what makes incremental adoption realistic. A team can add Kotlin to a Maven or Gradle project, write a single new class in Kotlin, and leave the other ten thousand files untouched. New features land in Kotlin, old code keeps compiling, and the boundary costs almost nothing at runtime because there is no separate runtime to bridge. Compare that with a language that requires a rewritten ecosystem, and the reason Kotlin won so much ground inside JVM shops becomes clear.

The design goals came from a specific frustration. JetBrains, the tooling company behind IntelliJ IDEA, started the project with a first commit in November 2010, unveiled it publicly as Project Kotlin in July 2011, and open sourced it under the Apache 2 license in February 2012. Their lead Dmitry Jemerov noted that most languages lacked the features the team wanted except Scala, whose slow compilation was a dealbreaker for everyday work. One of Kotlin's explicit goals was to compile as quickly as Java. Kotlin 1.0, the first stable release, arrived on February 15, 2016, and JetBrains committed to backward compatibility from that point on.

Interoperability also shows up in the details that irritate people on first contact. Kotlin distinguishes nullable from non-nullable types, so a String and a String? are different types and the compiler forces you to deal with the possibility of null. Kotlin emits nullability annotations that Java tooling can read, which is how an IDE can warn a Java caller about a method that returns null. The reverse trip is less smooth, because Java has no such distinction, and platform types from Java land in Kotlin as if they might be null. Senior developers learn to treat data crossing that boundary as untrusted input rather than a design flaw.

Android is where the strategy paid off at scale. At Google I/O 2017 Google announced first class support for Kotlin on Android, and on May 7, 2019 it declared Kotlin its preferred language for Android app developers. By 2024, more than 95 percent of the top 1,000 apps on Google Play contained Kotlin code. That migration was only possible because Android apps were already compiled to bytecode and shipped with a runtime that could execute a mix of languages. Nothing about the platform had to change for the language to be adopted.

The platform did keep evolving, and the compiler is now the interesting part. Kotlin 2.0, released in May 2024, shipped the K2 compiler, a from scratch rewrite of the frontend that unifies the JVM, JavaScript, WebAssembly, and native targets behind one pipeline. JetBrains reported compilation speed improvements of up to 94 percent in some projects and an analysis phase up to 376 percent faster. Before release the compiler was validated against roughly 10 million lines of code across 80,000 projects. Better tooling is not glamorous, but a faster compiler is precisely what made Scala lose developers a decade earlier, and JetBrains had clearly learned from that.

There is a practical lesson in all of this for anyone maintaining a Java system. The most successful new language of the last decade did not ask its users to abandon their platform, their libraries, or their build tools. It asked to share them. Compiling to the same bytecode as its predecessor turned adoption into a file by file decision instead of an architectural one, and that is a strategy worth copying whenever you are introducing a new technology into an existing system.

We use cookies for ads and analytics.