Kotlin vs. Java: All-purpose Uses and Android Apps
Kotlin and Java are two powerful general-purpose languages popular for Android and beyond. We’ll discuss their top features and differences, then focus on how to smoothly transition between the two.
Kotlin and Java are two powerful general-purpose languages popular for Android and beyond. We’ll discuss their top features and differences, then focus on how to smoothly transition between the two.
Gabriel is a senior Android engineer with eight years of experience building apps from scratch in Kotlin and Java, and facilitating conversions between the two languages. Gabriel has worked on multimillion-dollar apps and has industry experience at companies like HBO and Yahoo.
Previous Role
Senior Android EngineerPREVIOUSLY AT
It’s true that Java lost the Android battle to Kotlin, which is now Google’s preferred language and therefore better suited to new mobile apps. But both Kotlin and Java offer many strengths as general-purpose languages, and it’s important for developers to understand the language differences, for purposes such as migrating from Java to Kotlin. In this article, we will break down Kotlin’s and Java’s differences and similarities so you can make informed decisions and move seamlessly between the two.
Are Kotlin and Java Similar?
Indeed, the two languages have a lot in common from a high-level perspective. Both Kotlin and Java run on the Java Virtual Machine (JVM) instead of building directly to native code. And the two languages can call into each other easily: You can call Java code from Kotlin and Kotlin code from Java. Java can be used in server-side applications, databases, web front-end applications, embedded systems and enterprise applications, mobile, and more. Kotlin is similarly versatile: It targets the JVM , Android, JavaScript, and Kotlin/Native, and can also be used for server-side, web, and desktop development.
Java is a much more mature language than Kotlin, with its first release in 1996. Though Kotlin 1.0 was introduced much later, in 2016, Kotlin quickly became the official preferred language for Android development in 2019. Outside of Android, however, there is no recommendation to replace Java with Kotlin.
Year | Java | Kotlin |
---|---|---|
1995–2006 | JDK Beta, JDK 1.0, JDK 1.1, J2SE 1.2, J2SE 1.3, J2SE 1.4, J2SE 5.0, Java SE 6 | N/A |
2007 | Project Loom first commit | N/A |
2010 | N/A | Kotlin development started |
2011 | Java SE 7 | Kotlin project announced |
2012 | N/A | Kotlin open sourced |
2014 | Java SE 8 (LTS) | N/A |
2016 | N/A | Kotlin 1.0 |
2017 | Java SE 9 | Kotlin 1.2; Kotlin support for Android announced |
2018 | Java SE 10, Java SE 11 (LTS) | Kotlin 1.3 (coroutines) |
2019 | Java SE 12, Java SE 13 | Kotlin 1.4 (interoperability for Objective-C and Swift); Kotlin announced as Google’s preferred language for developers |
2020 | Java SE 14, Java SE 15 | N/A |
2021 | Java SE 16, Java SE 17 (LTS) | Kotlin 1.5, Kotlin 1.6 |
2022 | Java SE 18, JDK 19 | Kotlin 1.7 (alpha version of Kotlin K2 compiler), Kotlin 1.8 |
2023 | Java SE 20, Java SE 21, JDK 20, JDK 21 | Kotlin 1.9 |
2024 | Java SE 22 (scheduled) | Kotlin 2.0 (potentially) |
Kotlin vs. Java: Performance and Memory
Before detailing Kotlin’s and Java’s features, we’ll examine their performance and memory consumption as these factors are generally important considerations for developers and clients.
Kotlin, Java, and the other JVM languages, although not equal, are fairly similar in terms of performance, at least when compared to languages in other compiler families like GCC or Clang. The JVM was initially designed to target embedded systems with limited resources in the 1990s. The related environmental requirements led to two main constraints:
- Simple JVM bytecode: The current version of JVM, in which both Kotlin and Java are compiled, has only 205 instructions. In comparison, a modern x64 processor can easily support over 6,000 encoded instructions, depending on the counting method.
- Runtime (versus compile-time) operations: The multiplatform approach (“Write once and run anywhere”) encourages runtime (instead of compile-time) optimizations. In other words, the JVM translates the bulk of its bytecode into instructions at runtime. However, to improve performance, you may use open-source implementations of the JVM, such as HotSpot, which pre-compiles the bytecode to run faster through the interpreter.
With similar compilation processes and runtime environments, Kotlin and Java have only minor performance differences resulting from their distinct features. For example:
- Kotlin’s inline functions avoid a function call, improving performance, whereas Java invokes additional overhead memory.
- Kotlin’s higher-order functions avoid Java lambda’s special call to
InvokeDynamic
, improving performance. - Kotlin’s generated bytecode contains assertions for nullity checks when using external dependencies, slowing performance compared to Java.
Now let’s turn to memory. It is true in theory that the use of objects for base types (i.e., Kotlin’s implementation) requires more allocation than primitive data types (i.e., Java’s implementation). However, in practice, Java’s bytecode uses autoboxing and unboxing calls to work with objects, which can add computational overhead when used in excess. For example, Java’s String.format
method only takes objects as input, so formatting a Java int
will box it in an Integer
object before the call to String.format
.
On the whole, there are no significant Java and Kotlin differences related to performance and memory. You may examine online benchmarks which show minor differences in micro-benchmarks, but these cannot be generalized to the scale of a full production application.
Unique Feature Comparison
Kotlin and Java have core similarities, but each language offers different, unique features. Since Kotlin became Google’s preferred language for Android development, I’ve found extension functions and explicit nullability to be the most useful features. On the other hand, when using Kotlin, the Java features that I miss the most are the protected
keyword and the ternary operator.
Let’s examine a more detailed breakdown of features available in Kotlin versus Java. You may follow along with my examples using the Kotlin Playground or a Java compiler for a more hands-on learning approach.
Feature | Kotlin | Java | Description |
---|---|---|---|
Extension functions | Yes | No |
Allows you to extend a class or an interface with new functionalities such as added properties or methods without having to create a new class:
|
Smart casts | Yes | No |
Keeps track of conditions inside
if statements, safe casting automatically:
Kotlin also provides safe and unsafe cast operators:
|
Inline functions | Yes | No | Reduces overhead memory costs and improves speed by inlining function code (copying it to the call site): inline fun example() . |
Native support for delegation | Yes | No | Supports the delegation design pattern natively with the use of the by keyword: class Derived(b: Base) : Base by b . |
Type aliases | Yes | No | Provides shortened or custom names for existing types, including functions and inner or nested classes: typealias ShortName = LongNameExistingType . |
Non-private fields | No | Yes | Offers protected and default (also known as package-private ) modifiers, in addition to public and private modifiers. Java has all four access modifiers, while Kotlin is missing protected and the default modifier. |
Ternary operator | No | Yes |
Replaces an if/else statement with simpler and more readable code:
|
Implicit widening conversions | No | Yes |
Allows for automatic conversion from a smaller data type to a larger data type:
|
Checked exceptions | No | Yes |
Requires, at compile time, a method to catch exceptions with the
throws keyword or handles exceptions with a try-catch block.Note: Checked exceptions were intended to encourage developers to design robust software. However, they can create boilerplate code, make refactoring difficult, and lead to poor error handling when misused. Whether this feature is a pro or con depends on developer preference.
|
There is one topic I’ve intentionally excluded from this table: null safety in Kotlin versus Java. This topic warrants a more detailed Kotlin to Java comparison.
Kotlin vs. Java: Null Safety
In my opinion, non-nullability is one of the greatest Kotlin features. This feature saves time because developers don’t have to handle NullPointerException
s (which are RuntimeException
s).
In Java, by default, you can assign a null
value to any variable:
String x = null;
// Running this code throws a NullPointerException
try {
System.out.println("First character: " + x.charAt(0));
} catch (NullPointerException e) {
System.out.println("NullPointerException thrown!");
}
In Kotlin, on the other hand, we have two options, making a variable nullable or non-nullable:
var nonNullableNumber: Int = 1
// This line throws a compile-time error because you can't assign a null value
nonNullableNumber = null
var nullableNumber: Int? = 2
// This line does not throw an error since we used a nullable variable
nullableNumber = null
I use non-nullable variables by default, and minimize the use of nullable variables for best practices; these Kotlin versus Java examples are meant to demonstrate differences in the languages. Kotlin beginners should avoid the trap of setting variables to be nullable without a purpose (this can also happen when you convert Java code to Kotlin).
However, there are a few cases where you would use nullable variables in Kotlin:
Scenario | Example |
---|---|
You are searching for an item in a list that is not there (usually when dealing with the data layer). |
|
You want to initialize a variable during runtime, using lateinit . |
|
I was guilty of overusing lateinit
variables when I first got started with Kotlin. Eventually, I stopped using them almost completely, except when defining view bindings and variable injections in Android:
@Inject // With the Hilt library, this is initialized automatically
lateinit var manager: SomeManager
lateinit var viewBinding: ViewBinding
fun onCreate() { // i.e., Android onCreate
binding = ActivityMainBinding.inflate(layoutInflater, parentView, true)
// ...
}
On the whole, null safety in Kotlin provides added flexibility and an improved developer experience compared to Java.
Shared Feature Differences: Moving Between Java and Kotlin
While each language has unique features, Kotlin and Java share many features too, and it is necessary to understand their peculiarities in order to transition between the two languages. Let’s examine four common concepts that operate differently in Kotlin and Java:
Feature | Java | Kotlin |
---|---|---|
Data transfer objects (DTOs) |
Java records, which hold information about data or state and include
toString , equals , and hashCode methods by default, have been available since Java SE 15:
|
Kotlin data classes function similarly to Java records, with
toString , equals , and copy methods available:
|
Lambda expressions |
Java lambda expressions (available since Java 8) follow a simple
parameter -> expression syntax, with parentheses used for multiple parameters: (parameter1, parameter2) -> { code } :
|
Kotlin lambda expressions follow the syntax
{ parameter1, parameter2 -> code } and are always surrounded by curly braces:
|
Java threads make concurrency possible, and the java.util.concurrency package allows for easy multithreading through its utility classes. The Executor and ExecutorService classes are especially beneficial for concurrency. (Project Loom also offers lightweight threads.) | Kotlin coroutines, from the kotlinx.coroutines library, facilitate concurrency and include a separate library branch for multithreading. The memory manager in Kotlin 1.7.20 and later versions reduces previous limitations on concurrency and multithreading for developers moving between iOS and Android. | |
Static behavior in classes |
Java static members facilitate the sharing of code among class instances and ensure that only a single copy of an item is created. The
static keyword can be applied to variables, functions, blocks, and more:
|
Kotlin companion objects offer static behavior in classes, but the syntax is not as straightforward:
|
Of course, Kotlin and Java also have varying syntaxes. Discussing every syntax difference is beyond our scope, but a consideration of loops should give you an idea of the overall situation:
Loop Type | Java | Kotlin |
---|---|---|
for , using in
|
|
|
for , using until
|
|
|
forEach |
|
|
while |
|
|
An in-depth understanding of Kotlin features will assist in transitions between Kotlin and Java.
Android Project Planning: Additional Considerations
We’ve examined many important factors to think about when deciding between Kotlin and Java in a general-purpose context. However, no Kotlin versus Java analysis is complete without addressing the elephant in the room: Android. Are you making an Android application from scratch and wondering if you should use Java or Kotlin? Choose Kotlin, Google’s preferred Android language, without a doubt.
However, this question is moot for existing Android applications. In my experience across a wide range of clients, the two more important questions are: How are you treating tech debt? and How are you taking care of your developer experience (DX)?
So, how are you treating tech debt? If your Android app is using Java in 2023, your company is likely pushing for new features instead of dealing with tech debt. It’s understandable. The market is competitive and demands a fast turnaround cycle for app updates. But tech debt has a hidden effect: It causes increased costs with each update because engineers have to work around unstable code that is challenging to refactor. Companies can easily enter a never-ending cycle of tech debt and cost. It may be worth pausing and investing in long-term solutions, even if this means large-scale code refactors or updating your codebase to use a modern language like Kotlin.
And how are you taking care of your developers through DX? Developers require support across all levels of their careers:
- Junior developers benefit from proper resources.
- Mid-level developers grow through opportunities to lead and teach.
- Senior developers require the power to architect and implement beautiful code.
Attention to DX for senior developers is especially important since their expertise trickles down and affects all engineers. Senior developers love to learn and experiment with the latest technologies. Keeping up with newer trends and language releases will allow your team members to reach their greatest potential. This is important regardless of the team’s language choice, though different languages have varying timelines: With young languages like Kotlin, an engineer working on legacy code can fall behind trends in less than one year; with mature languages like Java, it will take longer.
Kotlin and Java: Two Powerful Languages
While Java has a wide range of applications, Kotlin has undeniably stolen its thunder as the preferred language for the development of new Android apps. Google has put all of its efforts into Kotlin, and its new technologies are Kotlin-first. Developers of existing apps might consider integrating Kotlin into any new code—IntelliJ comes with an automatic Java to Kotlin tool—and should examine factors that reach beyond our initial question of language choice.
The editorial team of the Toptal Engineering Blog extends its gratitude to Thomas Wuillemin for reviewing the code samples and other technical content presented in this article.
Further Reading on the Toptal Blog:
Understanding the basics
Can Kotlin be used with Java?
Yes, you can call Java code from Kotlin and Kotlin code from Java.
Are Java and Kotlin the same?
Java and Kotlin are two separate languages. Though they have certain similarities, such as their typical use cases (e.g., Android development), they have different features and syntaxes.
Is Kotlin replacing Java?
Kotlin is Google’s preferred language for Android development, but many Android apps still use Java. New Android apps should certainly use Kotlin, but the choice becomes more complex for existing Android apps using Java.
Why should I use Kotlin over Java?
It makes sense to use Kotlin over Java for new Android apps because Google’s latest technologies are Kotlin-first, and Kotlin has many attractive features, a large community, and extensive documentation.
Is Kotlin easier than Java?
Both Kotlin and Java are relatively beginner-friendly, with strong communities and documentation. In particular, Kotlin’s concise syntax and feature benefits, such as non-nullability, make it even easier to learn than Java.
Zagreb, Croatia
November 10, 2016
About the author
Gabriel is a senior Android engineer with eight years of experience building apps from scratch in Kotlin and Java, and facilitating conversions between the two languages. Gabriel has worked on multimillion-dollar apps and has industry experience at companies like HBO and Yahoo.
Previous Role
Senior Android EngineerPREVIOUSLY AT