Calling Kotlin from Java

    A Kotlin property is compiled to the following Java elements:

    • A getter method, with the name calculated by prepending the get prefix;
    • A setter method, with the name calculated by prepending the set prefix (only for var properties);
    • A private field, with the same name as the property name (only for properties with backing fields).

    For example, var firstName: String gets compiled to the following Java declarations:

    If the name of the property starts with is, a different name mapping rule is used: the name of the getter will be the same as the property name, and the name of the setter will be obtained by replacing is with set. For example, for a property isOpen, the getter will be called isOpen() and the setter will be called setOpen(). This rule applies for properties of any type, not just Boolean.

    Package-level functions

    All the functions and properties declared in a file app.kt inside a package org.example, including extension functions, are compiled into static methods of a Java class named org.example.AppKt.

    1. // app.kt
    2. package org.example
    3. class Util
    4. fun getTime() { /*...*/ }
    1. // Java
    2. new org.example.Util();
    3. org.example.AppKt.getTime();

    The name of the generated Java class can be changed using the @JvmName annotation:

    1. @file:JvmName("DemoUtils")
    2. package org.example
    3. class Util
    4. fun getTime() { /*...*/ }
    1. // Java
    2. new org.example.Util();
    3. org.example.DemoUtils.getTime();

    Having multiple files which have the same generated Java class name (the same package and the same name or the same annotation) is normally an error. However, the compiler has the ability to generate a single Java facade class which has the specified name and contains all the declarations from all the files which have that name. To enable the generation of such a facade, use the @JvmMultifileClass annotation in all of the files.

    1. // oldutils.kt
    2. @file:JvmName("Utils")
    3. @file:JvmMultifileClass
    4. package org.example
    5. fun getTime() { /*...*/ }
    1. // newutils.kt
    2. @file:JvmName("Utils")
    3. @file:JvmMultifileClass
    4. package org.example
    5. fun getDate() { /*...*/ }
    1. // Java
    2. org.example.Utils.getTime();
    3. org.example.Utils.getDate();

    Instance fields

    If you need to expose a Kotlin property as a field in Java, annotate it with the @JvmField annotation. The field will have the same visibility as the underlying property. You can annotate a property with @JvmField if it has a backing field, is not private, does not have open, override or const modifiers, and is not a delegated property.

    1. class User(id: String) {
    2. @JvmField val ID = id
    3. }
    1. // Java
    2. class JavaClient {
    3. public String getID(User user) {
    4. return user.ID;
    5. }
    6. }

    properties are also exposed as fields. The visibility of the field will be the same as the visibility of lateinit property setter.

    Static fields

    Kotlin properties declared in a named object or a companion object will have static backing fields either in that named object or in the class containing the companion object.

    Usually these fields are private but they can be exposed in one of the following ways:

    • annotation;
    • lateinit modifier;
    • const modifier.

    Annotating such a property with @JvmField makes it a static field with the same visibility as the property itself.

    1. class Key(val value: Int) {
    2. companion object {
    3. val COMPARATOR: Comparator<Key> = compareBy<Key> { it.value }
    4. }
    5. }
    1. // Java
    2. Key.COMPARATOR.compare(key1, key2);
    3. // public static final field in Key class

    A late-initialized property in an object or a companion object has a static backing field with the same visibility as the property setter.

    1. object Singleton {
    2. lateinit var provider: Provider
    3. }
    1. // Java
    2. Singleton.provider = new Provider();
    3. // public static non-final field in Singleton class

    Properties declared as const (in classes as well as at the top level) are turned into static fields in Java:

    In Java:

    1. int const = Obj.CONST;
    2. int max = ExampleKt.MAX;
    3. int version = C.VERSION;

    As mentioned above, Kotlin represents package-level functions as static methods. Kotlin can also generate static methods for functions defined in named objects or companion objects if you annotate those functions as . If you use this annotation, the compiler will generate both a static method in the enclosing class of the object and an instance method in the object itself. For example:

    1. class C {
    2. companion object {
    3. @JvmStatic fun callStatic() {}
    4. fun callNonStatic() {}
    5. }
    6. }

    Now, callStatic() is static in Java, while callNonStatic() is not:

    1. C.callStatic(); // works fine
    2. C.callNonStatic(); // error: not a static method
    3. C.Companion.callStatic(); // instance method remains
    4. C.Companion.callNonStatic(); // the only way it works

    Same for named objects:

    1. object Obj {
    2. @JvmStatic fun callStatic() {}
    3. fun callNonStatic() {}
    4. }
    1. Obj.callStatic(); // works fine
    2. Obj.callNonStatic(); // error
    3. Obj.INSTANCE.callNonStatic(); // works, a call through the singleton instance
    4. Obj.INSTANCE.callStatic(); // works too

    Starting from Kotlin 1.3, @JvmStatic applies to functions defined in companion objects of interfaces as well. Such functions compile to static methods in interfaces. Note that static method in interfaces were introduced in Java 1.8, so be sure to use the corresponding targets.

    1. interface ChatBot {
    2. companion object {
    3. @JvmStatic fun greet(username: String) {
    4. println("Hello, $username")
    5. }
    6. }
    7. }

    @JvmStatic annotation can also be applied on a property of an object or a companion object making its getter and setter methods static members in that object or the class containing the companion object.

    Default methods in interfaces

    Starting from JDK 1.8, interfaces in Java can contain . To make all non-abstract members of Kotlin interfaces default for the Java classes implementing them, compile the Kotlin code with the -Xjvm-default=all compiler option.

    Here is an example of a Kotlin interface with a default method:

    1. // compile with -Xjvm-default=all
    2. interface Robot {
    3. fun move() { println("~walking~") } // will be default in the Java interface
    4. fun speak(): Unit
    5. }

    The default implementation is available for Java classes implementing the interface.

    1. //Java implementation
    2. public class C3PO implements Robot {
    3. // move() implementation from Robot is available implicitly
    4. @Override
    5. public void speak() {
    6. System.out.println("I beg your pardon, sir");
    7. }
    8. }
    1. C3PO c3po = new C3PO();
    2. c3po.move(); // default implementation from the Robot interface
    3. c3po.speak();

    Implementations of the interface can override default methods.

    1. //Java
    2. public class BB8 implements Robot {
    3. //own implementation of the default method
    4. @Override
    5. public void move() {
    6. System.out.println("~rolling~");
    7. }
    8. @Override
    9. public void speak() {
    10. System.out.println("Beep-beep");
    11. }

    If there are clients that use your Kotlin interfaces compiled without the new -Xjvm-default=all option, then they can be incompatible with the same code compiled with this option.

    To avoid breaking the compatibility with such clients, compile your Kotlin code in the compatibility mode by specifying the -Xjvm-default=all-compatibility compiler option. In this case, all the code that uses the previous version will work fine with the new one. However, the compatibility mode adds some overhead to the resulting bytecode size.

    There is no need to consider compatibility for new interfaces, as no clients have used them before. You can minimize the compatibility overhead by excluding these interfaces from the compatibility mode. To do this, annotate them with the @JvmDefaultWithoutCompatibility annotation. Such interfaces compile the same way as with -Xjvm-default=all.

    Additionally, in the all-compatibility mode you can use @JvmDefaultWithoutCompatibility to annotate all interfaces which are not exposed in the public API and therefore aren’t used by the existing clients.

    Visibility

    The Kotlin visibility modifiers map to Java in the following way:

    • private members are compiled to private members;
    • private top-level declarations are compiled to package-local declarations;
    • protected remains protected (note that Java allows accessing protected members from other classes in the same package and Kotlin doesn’t, so Java classes will have broader access to the code);
    • internal declarations become public in Java. Members of internal classes go through name mangling, to make it harder to accidentally use them from Java and to allow overloading for members with the same signature that don’t see each other according to Kotlin rules;
    • public remains public.

    KClass

    Sometimes you need to call a Kotlin method with a parameter of type KClass. There is no automatic conversion from Class to KClass, so you have to do it manually by invoking the equivalent of the Class<T>.kotlin extension property:

    1. kotlin.jvm.JvmClassMappingKt.getKotlinClass(MainView.class)

    Sometimes we have a named function in Kotlin, for which we need a different JVM name in the byte code. The most prominent example happens due to type erasure:

    1. fun List<String>.filterValid(): List<String>
    2. fun List<Int>.filterValid(): List<Int>

    These two functions can not be defined side-by-side, because their JVM signatures are the same: filterValid(Ljava/util/List;)Ljava/util/List;. If we really want them to have the same name in Kotlin, we can annotate one (or both) of them with @JvmName and specify a different name as an argument:

    1. fun List<String>.filterValid(): List<String>
    2. fun List<Int>.filterValid(): List<Int>

    From Kotlin they will be accessible by the same name filterValid, but from Java it will be filterValid and filterValidInt.

    To change the names of generated accessor methods for properties without explicitly implemented getters and setters, you can use @get:JvmName and @set:JvmName:

    1. @get:JvmName("x")
    2. @set:JvmName("changeX")
    3. var x: Int = 23

    Overloads generation

    Normally, if you write a Kotlin function with default parameter values, it will be visible in Java only as a full signature, with all parameters present. If you wish to expose multiple overloads to Java callers, you can use the @JvmOverloads annotation.

    The annotation also works for constructors, static methods, and so on. It can’t be used on abstract methods, including methods defined in interfaces.

    1. class Circle @JvmOverloads constructor(centerX: Int, centerY: Int, radius: Double = 1.0) {
    2. @JvmOverloads fun draw(label: String, lineWidth: Int = 1, color: String = "red") { /*...*/ }
    3. }

    For every parameter with a default value, this will generate one additional overload, which has this parameter and all parameters to the right of it in the parameter list removed. In this example, the following will be generated:

    1. // Constructors:
    2. Circle(int centerX, int centerY, double radius)
    3. Circle(int centerX, int centerY)
    4. // Methods
    5. void draw(String label, int lineWidth, String color) { }
    6. void draw(String label, int lineWidth) { }
    7. void draw(String label) { }

    Note that, as described in , if a class has default values for all constructor parameters, a public no-argument constructor will be generated for it. This works even if the @JvmOverloads annotation is not specified.

    Checked exceptions

    As we mentioned above, Kotlin does not have checked exceptions. So, normally, the Java signatures of Kotlin functions do not declare exceptions thrown. Thus if we have a function in Kotlin like this:

    1. // example.kt
    2. package demo
    3. fun writeToFile() {
    4. /*...*/
    5. throw IOException()
    6. }

    And we want to call it from Java and catch the exception:

    1. // Java
    2. try {
    3. demo.Example.writeToFile();
    4. }
    5. catch (IOException e) { // error: writeToFile() does not declare IOException in the throws list
    6. // ...
    7. }

    we get an error message from the Java compiler, because writeToFile() does not declare IOException. To work around this problem, use the annotation in Kotlin:

    1. @Throws(IOException::class)
    2. fun writeToFile() {
    3. /*...*/
    4. throw IOException()
    5. }

    Null-safety

    When calling Kotlin functions from Java, nobody prevents us from passing 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.

    When Kotlin classes make use of , there are two options of how their usages are seen from the Java code. Let’s say we have the following class and two functions that use it:

    1. class Box<out T>(val value: T)
    2. interface Base
    3. class Derived : Base
    4. fun boxDerived(value: Derived): Box<Derived> = Box(value)
    5. fun unboxBase(box: Box<Base>): Base = box.value

    A naive way of translating these functions into Java would be this:

    1. Box<Derived> boxDerived(Derived value) { ... }
    2. Base unboxBase(Box<Base> box) { ... }

    The problem is that in Kotlin we can say unboxBase(boxDerived("s")), but in Java that would be impossible, because in Java the class Box is invariant in its parameter T, and thus Box<Derived> is not a subtype of Box<Base>. To make it work in Java we’d have to define unboxBase as follows:

    1. Base unboxBase(Box<? extends Base> box) { ... }

    Here we make use of Java’s wildcards types (? extends Base) to emulate declaration-site variance through use-site variance, because it is all Java has.

    To make Kotlin APIs work in Java we generate Box<Super> as Box<? extends Super> for covariantly defined Box (or Foo<? super Bar> for contravariantly defined Foo) when it appears as a parameter. When it’s a return value, we don’t generate wildcards, because otherwise Java clients will have to deal with them (and it’s against the common Java coding style). Therefore, the functions from our example are actually translated as follows:

    1. // return type - no wildcards
    2. Box<Derived> boxDerived(Derived value) { ... }
    3. // parameter - wildcards
    4. Base unboxBase(Box<? extends Base> box) { ... }

    If we need wildcards where they are not generated by default, we can use the @JvmWildcard annotation:

    1. fun boxDerived(value: Derived): Box<@JvmWildcard Derived> = Box(value)
    2. // is translated to
    3. // Box<? extends Derived> boxDerived(Derived value) { ... }

    On the other hand, if we don’t need wildcards where they are generated, we can use @JvmSuppressWildcards:

    1. fun unboxBase(box: Box<@JvmSuppressWildcards Base>): Base = box.value
    2. // is translated to
    3. // Base unboxBase(Box<Base> box) { ... }

    Translation of type Nothing

    The type is special, because it has no natural counterpart in Java. Indeed, every Java reference type, including java.lang.Void, accepts null as a value, and Nothing doesn’t accept even that. So, this type cannot be accurately represented in the Java world. This is why Kotlin generates a raw type where an argument of type Nothing is used:

    1. fun emptyList(): List<Nothing> = listOf()
    2. // List emptyList() { ... }