Kotlin Top-Level Functions and Properties for Java Interoperability
When Kotlin files are compiled, top-level functions declared within them are converted into static methods. These methods are housed in a class named after the Kotlin file with a Kt suffix. For instance, a file named MathUtils.kt will produce a class named MathUtilsKt.
// MathUtils.kt
package com.example.utils
fun calculateProduct(x: Int, y: Int): Int {
return x * y
}
The equivalent compiled Java bytecode looks like:
public class MathUtilsKt {
public static int calculateProduct(int x, int y) {
return x * y;
}
}
Invoking Top-Level Functions from Java
To call a top-level Kotlin function from Java, use the generated class name followed by the method name.
// ClientApp.java
import com.example.utils.MathUtilsKt;
public class ClientApp {
public static void main(String[] args) {
int product = MathUtilsKt.calculateProduct(6, 7);
System.out.println("Calculated product: " + product);
}
}
Customizing the Generated Class Name with @JvmName
The @JvmName annotation modifies the name of the generated wrapper class. By placing @file:JvmName("CalculatorHelper") at the top of the Kotlin file, the compiler changes the facade class name from MathUtilsKt to CalculatorHelper.
// MathUtils.kt
@file:JvmName("CalculatorHelper")
package com.example.utils
fun calculateProduct(x: Int, y: Int): Int {
return x * y
}
The Java implementation then imports and calls the method using the new class name:
// ClientApp.java
import com.example.utils.CalculatorHelper;
public class ClientApp {
public static void main(String[] args) {
int product = CalculatorHelper.calculateProduct(6, 7);
System.out.println("Calculated product: " + product);
}
}
Merging Multiple Files with @JvmMultifileClass
If multiple Kotlin files use @JvmName to generate the same facade class name, a compilation conflict occurs. The @JvmMultifileClass annotation resolves this by instructing the compiler to generate a single facade class containing the top-level declarations from all associated files.
// GeometryOps.kt
@file:JvmName("CalculatorHelper")
@file:JvmMultifileClass
package com.example.utils
fun calculateProduct(x: Int, y: Int): Int {
return x * y
}
// AlgebraOps.kt
@file:JvmName("CalculatorHelper")
@file:JvmMultifileClass
package com.example.utils
fun computeSum(x: Int, y: Int): Int {
return x + y
}
Both functions become accessible through the unified CalculatorHelper class in Java:
// ClientApp.java
import com.example.utils.CalculatorHelper;
public class ClientApp {
public static void main(String[] args) {
int product = CalculatorHelper.calculateProduct(6, 7);
int sum = CalculatorHelper.computeSum(6, 7);
System.out.println("Product: " + product + ", Sum: " + sum);
}
}
Accessing Kotlin Constants from Java
Kotlin properties marked with the const modifier are compiled into static fields in Java. This applies to top-level properties, properties inside objects, and properties inside companion objects.
// Constants.kt
package com.example.utils
const val MAX_LIMIT = 500
object Config {
const val THRESHOLD = 10
}
class SystemSettings {
companion object {
const val VERSION_CODE = 2
}
}
These constants are accessed as standard static variables in Java:
// ClientApp.java
import com.example.utils.ConstantsKt;
public class ClientApp {
public static void main(String[] args) {
int limit = ConstantsKt.MAX_LIMIT;
int threshold = Config.THRESHOLD;
int version = SystemSettings.VERSION_CODE;
System.out.println("Limit: " + limit + ", Threshold: " + threshold + ", Version: " + version);
}
}