JVM has some platform limitations that are a result of its implementation. Consider the following two functions:
fun List<Long>.average() = sum().toDouble() / size
fun List<Int>.average() = sum().toDouble() / size
Each of these functions is an extension on List with a different type argument. This is perfectly fine for Kotlin, but not when targeted for the JVM platform. Due to type erasure, both these functions on JVM are considered methods called average with a single parameter of type List. So, having them both defined in the same file leads to a platform name clash.
A simple solution to this problem is to use the JvmName annotation, which changes the name that will be used for this function under the hood on the JVM platform. Usage will not change in Kotlin, but if you want to use these functions from a non-Kotlin JVM language, you need to use the names specified in the annotation.
@JvmName("averageLongList")
fun List<Long>.average() = sum().toDouble() / size
@JvmName("averageIntList")
fun List<Int>.average() = sum().toDouble() / size
fun main() {
val ints: List<Int> = List(10) { it }
println(ints.average()) // 4.5
val longs: List<Long> = List(10) { it.toLong() }
println(longs.average()) // 4.5
}
The JvmName annotation is useful for resolving name conflicts, but there is another case where it is used much more often. As I explained in the Kotlin Essentials book, for all Kotlin top-level functions and properties on JVM, a class is generated whose name is the file name with the "Kt" suffix. Top-level functions and properties are compiled to static JVM functions in this class and thus can be used from Java.
package test
const val e = 2.71
fun add(a: Int, b: Int) = a + b
// Compiles to the analog of the following Java codepackagetest;publicfinalclassTestKt{publicstaticfinaldouble e =2.71;publicstaticfinalintadd(int a,int b){return a + b;}}
// Usage from JavapublicclassJavaClass{publicstaticvoidmain(String[] args){System.out.println(TestKt.e);// 2.71int res =TestKt.add(1,2);System.out.println(res);// 3}}
This auto-generated name is not always what we want to use. Often we would prefer to specify a custom name, in which case we should use the JvmName annotation for the file. As I explained in the Annotation targets section, file annotations must be placed at the beginning of the file, even before the package definition, and they must use the file target. The name that we will specify in the JvmName file annotation will be used for the class that stores all the top-level functions and properties.
@file:JvmName("Math")
package test
const val e = 2.71
fun add(a: Int, b: Int) = a + b
// Compiles to the analog of the following Java codepackagetest;publicfinalclassMath{publicstaticfinaldouble e =2.71;publicstaticfinalintadd(int a,int b){return a + b;}}
// Usage from JavapublicclassJavaClass{publicstaticvoidmain(String[] args){System.out.println(Math.e);// 2.71int res =Math.add(1,2);System.out.println(res);// 3}}
JvmMultifileClass
Because all functions or fields on JVM must be located in a class, it is common practice in Java projects to make huge classes like Math or Collections that are holders for static elements. In Kotlin, we use top-level functions instead, which offers us the convenience that we don’t need to collect all these functions and properties in the same file. However, when we design Kotlin code for use from Java, we might want to collect elements from multiple files that define the same package in the same generated class. For that, we need to use the JvmMultifileClass annotation next to JvmName.
// Usage from Javaimportdemo.Utils;publicclassJavaClass{publicstaticvoidmain(String[] args){Utils.foo();Utils.bar();}}
JvmOverloads
Another inconsistency between Java and Kotlin is a result of another Java limitation. Java, unlike most modern programming languages, does not support named optional arguments, therefore default Kotlin arguments cannot be used in Java.
class Pizza(
val tomatoSauce: Int = 1,
val cheese: Int = 0,
val ham: Int = 0,
val onion: Int = 0,
)
class EmailSender {
fun send(
receiver: String,
title: String = "",
message: String = "",
) {
/*...*/
}
}
Note that when all constructor parameters are optional, two constructors are generated: one with all the parameters, and one that uses only default values.
The best that can be offered for Java is an implementation of the telescoping constructor pattern so that Kotlin can generate different variants of a constructor or function for a different number of arguments. This is not done by default in order to avoid generating methods that are not used. So, you need to use the JvmOverloads annotation before a function to make Kotlin generate different variants with different numbers of expected arguments.
class Pizza @JvmOverloads constructor(
val tomatoSauce: Int = 1,
val cheese: Int = 0,
val ham: Int = 0,
val onion: Int = 0,
)
class EmailSender {
@JvmOverloads
fun send(
receiver: String,
title: String = "",
message: String = "",
) {
/*...*/
}
}
Notice that Kotlin does not generate all possible combinations of parameters; it only generates one additional variant for each optional parameter.
Do not confuse JvmOverloads with JVM overlords.
[^53_1]: A companion object is also an object declaration, as I explained in the Kotlin Essentials book.
Marcin Moskala is a highly experienced developer and Kotlin instructor as the founder of Kt. Academy, an official JetBrains partner specializing in Kotlin training, Google Developers Expert, known for his significant contributions to the Kotlin community. Moskala is the author of several widely recognized books, including "Effective Kotlin," "Kotlin Coroutines," "Functional Kotlin," "Advanced Kotlin," "Kotlin Essentials," and "Android Development with Kotlin."
Beyond his literary achievements, Moskala is the author of the largest Medium publication dedicated to Kotlin. As a respected speaker, he has been invited to share his insights at numerous programming conferences, including events such as Droidcon and the prestigious Kotlin Conf, the premier conference dedicated to the Kotlin programming language.
Software architect with 15 years of experience, currently working on building infrastructure for AI. I think Kotlin is one of the best programming languages ever created.
Owen has been developing software since the mid 1990s and remembers the productivity of languages such as Clipper and Borland Delphi.
Since 2001, He moved to Web, Server based Java and the Open Source revolution.
With many years of commercial Java experience, He picked up on Kotlin in early 2015.
After taking detours into Clojure and Scala, like Goldilocks, He thinks Kotlin is just right and tastes the best.
Owen enthusiastically helps Kotlin developers continue to succeed.