DEV Community

Isaac
Isaac

Posted on

Using @JvmOverloads and @JvmStatic for Kotlin interoperability with Java

Kotlin promises interoperability with Java. Today(31/03/2020), I found out that we sometimes need to do a little extra to make interoperability with Java seamless and natural using a couple of annotations. I never really had a use case for Java interoperability, mainly because I have been working in Kotlin-only projects since 2018.

I had the chance to rewrite a simple open source library in Kotlin. During the rewrite, I failed to consider that there could be Android apps written in Java that already depend on the library till the owner of the library drew my attention to that fact. In order to not break existing applications that already depend on this library, I had to find a solution. And that's what led me to @JvmOverloads and @JvmStatic annotations.

The Java implementation of the library exposes a single static method with three(3) overloads. Rewriting this in Kotlin was quite simple.

// Shutdown.java
public static void now(Activity context){
        if (context != null){
            init(context, DEFAULT_MESSAGE, DEFAULT_TIMEOUT);
        }
    }

    public static void now(Activity context, String message){
        if (context != null && !message.isEmpty()){
            init(context, message, DEFAULT_TIMEOUT);
        }
    }

    public static void now(Activity context, long timeout){
        if (context != null && timeout != 0){
            init(context, DEFAULT_MESSAGE, timeout);
        }
    }

    public static void now(Activity context, String message, long timeout){
        if (context != null && !message.isEmpty() && timeout != 0){
            init(context, message, timeout);
        }
    }
Enter fullscreen mode Exit fullscreen mode
// Shutdown.kt

...// inside companion object

fun now(context: Activity?, message: String = DEFAULT_MESSAGE, timeout: Long = DEFAULT_TIME_OUT) {
    context?.let { init(context, message, timeout) }
}
Enter fullscreen mode Exit fullscreen mode

Calling the Kotlin implementation of now(context) from a Kotlin class works without issues. Calling this Kotlin implementation from a Java class will show you the red squiggly line.

// SomeActivityClass.java

Shutdown.now(this); // no issues
Enter fullscreen mode Exit fullscreen mode
//SomeActivityClass.kt

Shutdown.Companion.now(this); // Compile time error. Breaking change?? 😱
Enter fullscreen mode Exit fullscreen mode

The compile-time error message read that three(3) arguments were expected but only one(1) was found. Which was strange because I provided default values to the other two arguments in Kotlin. Upon further investigation, I found that the Kotlin compiler wasn't generating the overloads for now(context) so it was impossible to omit some arguments when calling from Java. In comes the @JvmOverloads annotation. This annotation essentially instructs the Kotlin compiler to generate overloads for now(context).

// Shutdown.kt

@JvmOverloads
fun now(context: Activity?, message: String = DEFAULT_MESSAGE, timeout: Long = DEFAULT_TIMEOUT)
    context?.let { init(it, message, timeout) }        
}
Enter fullscreen mode Exit fullscreen mode

Adding @JvmOverloads resolves the compile-time error. And now we can call now(context) from Java with one(1) or more arguments. But the Java usage still didn't feel natural. It didn't feel like a static method call. And that's what led me to @JvmStatic. This annotation tells the Kotlin compiler to generate an extra static method for now(context).

// Shutdown.kt

@JvmOverloads
@JvmStatic
fun now(context: Activity?, message: String = DEFAULT_MESSAGE, timeout: Long = DEFAULT_TIMEOUT)
    context?.let { init(it, message, timeout) }        
}
Enter fullscreen mode Exit fullscreen mode
// SomeActivity.java

Shutdown.now(this);
Enter fullscreen mode Exit fullscreen mode

Now the Java client doesn't need to access Companion in order to use now(context) from Java. Order has finally been restored in our Java client after the Kotlin rewrite. No more breaking changes! You can find the Pull Request here, if interested.

Thanks to Eyram A. Agbe, Raymond(creator of Playlistor), Waheed Derby and Wilfred for taking the time to review this article.

Top comments (0)