Java Interoperability - Calling Java from Kotlin
Since Kotlin was designed with full interoperability in mind, it allows us to easily use Java classes, methods, and fields from within Kotlin. This seamless integration makes it simple to reuse existing Java codebases while writing modern Kotlin code. In this article, we will understand how Kotlin interacts with Java.
Accessing Getters and Setters
The getters and setters of all the types defined within the Java class are represented as properties in Kotlin. Hence, to access the getters and setters of a data member of a Java class, it must reference as a property within Kotlin.
Example - myjava.java:
public class MyJava {
private int value = 5;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
Example - mykotlin.kt:
fun main() {
val obj = MyJava()
println(obj.value) // Uses getValue() under the hood
}
Output:
5
Kotlin recognizes getValue() and setValue() as property access for value, making the syntax cleaner and more idiomatic.
Calling Java Methods from Kotlin
Calling the Java methods from within Kotlin is a straightforward concept. The types of arguments provided are the same in both Java and Kotlin and the same is the case with the return type of the function. The only exception to this rule is the void return type. Those functions in Java that have a void return type, return a Unit type in Kotlin. So this value can be stored in Kotlin as a Unit exists as a type.
Example - myjava.java:
public class MyJava {
public void showMessage() {
System.out.println("The sum of two numbers is 9");
}
}
Example - mykotlin.kt:
fun main() {
val obj = MyJava()
val result: Unit = obj.showMessage()
}
Output:
The sum of two numbers is 9
Handling Kotlin Keywords as Java Identifiers
Sometimes, Java method names may conflict with Kotlin keywords like object, sealed, or any. Kotlin allows us to call such methods using backticks.
Example - Java:
public class XYZ {
public void any() {
System.out.println("Java method named 'any'");
}
}
Example - Kotlin:
fun main() {
val obj = XYZ()
obj.`any`() // Backtick allows use of the Kotlin keyword
}
Accessing Java Static Members
The static members of a class in Java become the members of a companion object in Kotlin. However, these companion objects cannot be used directly in expressions. To access, its members use the fully qualified name of the member as defined in Java.
Example - myjava.java:
public class MyJava {
public static String message = "Call successful";
}
Example - mykotlin.kt:
fun main() {
println(MyJava.message)
}
Output:
Call successful
Java Arrays vs Kotlin Arrays
Kotlin treats arrays differently from Java.
- Kotlin arrays are invariant, meaning an Array<String> cannot be assigned to an Array<Any>.
- Java arrays are covariant, which allows assigning a String[] to an Object[].
To ensure performance and compatibility, Kotlin provides specialized classes for primitive arrays like IntArray, ByteArray, etc. These map directly to Java primitive arrays.
Example - myjava.java:
public class MyJava {
public static int sum(int[] arr) {
int result = 0;
for (int i : arr) result += i;
return result;
}
}
Example - mykotlin.kt:
fun main() {
val arr = intArrayOf(1, 2, 3, 4, 5, 6)
println("The sum of an array is ${MyJava.sum(arr)}")
}
Output:
The sum of an array is 21
Java varargs
Java supports the concept of variable-length arguments in functions i.e when the number of arguments to a function is not known in advance, but their type is known, we declare a varargs parameter. Kotlin doesn't provide varargs parameters, however, to be fully operational with Java, it supports a special spread operator (*) to call the functions which have varargs parameters.
Example - myjava.java:
public class MyJava {
public static void printStrings(String... args) {
for (String str : args)
System.out.print(str + " ");
}
}
Example - mykotlin.kt:
fun main() {
val arr = arrayOf("Geeks", "10", "20", "30")
MyJava.printStrings(*arr) // Spread operator
}
Output:
Geeks 10 20 30
Java and Kotlin Mapped Types
Types in Kotlin are different from the types in Java. However, to maintain interoperability Kotlin provides a mapping from Java types to Kotlin types. This mapping takes place at compile time and no significant change in performance at runtime is observed.
Java primitive types are mapped to the following primitive types:
Java Type | Kotlin Type |
---|---|
byte | kotlin.Byte |
short | kotlin.Short |
int | kotlin.Int |
long | kotlin.Long |
char | kotlin.Char |
float | kotlin.Float |
double | kotlin.Double |
boolean | kotlin.Boolean |
Some of the built-in classes are defined in java.lang package is also mapped to Kotlin classes.
Java Type | Kotlin Type |
---|---|
java.lang.Object | kotlin.Any! |
java.lang.Cloneable | kotlin.Cloneable! |
java.lang.Comparable | kotlin.Comparable! |
java.lang.Enum | kotlin.Enum! |
java.lang.annotation | kotlin.Annotation! |
java.lang.CharSequence | kotlin.CharSequence |
java.lang.String | kotlin.String! |
java.lang.Number | kotlin.Number! |
java.lang.Throwable | kotlin.Throwable! |
The boxed types of Java's primitive data types are mapped to nullable types in Kotlin.
Java Type | Kotlin Type |
---|---|
java.lang.Byte | kotlin.Byte? |
java.lang.Short | kotlin.Short? |
java.lang.Integer | kotlin.Int? |
java.lang.Long | kotlin.Long? |
java.lang.Character | kotlin.Char? |
java.lang.Float | kotlin.Float? |
java.lang.Double | kotlin.Double? |
java.lang.Boolean | kotlin.Boolean? |