Calling Java code from Kotlin
In this section we describe some details about calling Java code from Kotlin. In most cases, you just use it:
If a Java method returns void, it will return Unit when called from Kotlin. If, by any chance, someone uses that return value, it will be assigned at the call site by the Kotlin compiler, since the value itself is known in advance (being Unit.VALUE).
Escaping for Java identifiers that are keywords in Kotlin
Some of the Kotlin keywords are valid identifiers in Java: in, object, is, etc. It may happen that some of your favorite Java libraries use these words as names for methods. In this situation you still can call those methods from Kotlin, but you need to escape their names with backticks:
Any reference in Java may be null. So, all the Java methods called from Kotlin return nullable references (except for those annotated with @NotNull). This allows Kotlin to keep the guarantee of having no NullPointerExceptions unless they are explicitly thrown by Kotlin code or caused by something inside Java code called from Kotlin.
Consider the following examples:
In Kotlin, all exceptions are unchecked, meaning that the compiler does not force you to catch any of them. So, when you call a Java method that declares a checked exception, Kotlin does not force you to do anything:
Java generics in Kotlin
- Java's wildcards are converted into type projections
- Foo<? extends Bar> becomes Foo<out Bar>
- Foo<? super Bar> becomes Foo<in Bar>
- Java's raw types are converted into star projections
- List becomes List<*>, i.e. List<out Any?>
Like Java's, Kotlin's generics are not retained at runtime, i.e. objects do not carry information about actual type arguments passed to their constructors, i.e. ArrayList<Integer>() is indistinguishable from ArrayList<Character>(). This makes it impossible to perform is-checks that take generics into account. Kotlin only allows is-checks for star-projected generic types:
Arrays in Kotlin are invariant, unlike Java. This means that Kotlin does not let us assign an Array<String> to an Array<Any>, which prevents a possible runtime failure. Neither does it allow us to pass an array of a subclass as an array of superclass to a Java method. In most cases, this should not be a major obstacle, but if one really needs to pass an array in a covariant way, they may cast explicitly.
On the Java platform, having a generic class Array to represent arrays leads to a lot of boxing/unboxing operations. As arrays are mostly use where performance is critical, we introduced a workaround for this issue and defined classes IntArray, DoubleArray, CharArray and so on, which are not related to the Array class and are compiled down to Java's primitive arrays.
When Java types are imported into Kotlin, all the references of type java.lang.Object are turned into Any?, for any reference may be used there.
The big difference between java.lang.Object and Any is that Any does not declare any members at all. This is due to the inheritance rules in Kotlin. Now, what do we do if we need our toString(), equals() etc?
toString() is declared as an extension function that looks for an instance function named toString and calls it. If there's no toString it returns some default like this.javaClass.getName() + "@" + System.identityHashCode(this).
From the programmer's perspective almost nothing changes compared to Java: all the existing toString() implementations work, and when you need a custom toString for your class, you simply put if there:
You don't have to make it virtual, and you are allowed to put override there only if one of the superclasses declares it virtual.
In Kotlin, == stands for a guarded call to equals(). The expression on the left-hand side must have a method named equals that takes one parameter of type Any? and returns Boolean. Thus, all the Java objects have it out of the box. On the other hand, there's an extension function to Any? that performs the same kind of lookup as toString().
hashCode() works for Java objects.
In the upcoming Kotlin standard library we plan to have a Hashable interface that is required for something to be put into a non-identity hash-map.
Effective Java Item 69 kindly suggests to Prefer concurrency utilities to wait and notify. Thus, these methods are not available on references of type Any, only on Java objects.
To retrieve the type information from an object, one uses the javaClass extension property.
Instead of Java's Foo.class, use javaClass<Foo>().
finalize() can be overridden exactly like toString()
clone() can be overridden like toString() but with specifying Cloneable as a supertype. Do not forget about Effective Java Item 11: Override clone judiciously.
At most one Java-class (and as many Java interfaces as you like) can be a supertype for a class in Kotlin. This class must go first in the supertype list.
Static members of Java classes form "class objects" for these classes. One cannot pass such a "class object" around as a value, but can access the members explicitly, for example
Calling Kotlin code from Java
We plan to target more platforms in the future, but currently, Kotlin is only compiled for the Java platform. This means that the compiler generates Java bytecode, and thus the code that we write in Kotlin can be called from Java. There are some concepts in Kotlin that are not available in Java, though. In this section, we briefly describe how these concepts are mapped to Java concepts.
All the functions and properties declared inside a package org.foo.bar are put into a Java class named org.foo.bar.BarPackage.
For the root package (the one that's called a "default package" in Java), a class named _DefaultPackage is created.
And we want to call it from Java and catch the exception:
we get an error message from the Java compiler, because foo() does not declare IOException. Now, what should we do? There are a few options:
- Option one (suggested in the comments below) is to create a pseudo-throwing function in Java:
And then write:
- Option two is to catch Throwable and do an instanceof check. This is not very elegant, but will work.
- Option three is to write a wrapper function in Java:
Now, you can call foo() instead of demo.DemoPackage.foo(), and catch the exception.
- Option four is to make Kotlin put a throws list to the foo()'s signature with the throws annotation:
When calling Kotlin functions from Java, nobody prevents us from passing a null as a non-null parameter. That's why Kotlin generates runtime checks for all public functions that expect non-nulls. This way we get a NullPointerException in the Java code immediately.
Property getters are turned into get-methods, and setters – into set-methods.