use static Java methods as static (or singleton) properties in Kotlin

653 views Asked by At

My SDK exposes a Java interface that has only static methods, e.g.

public interface MyDevice { 
  public static void setLocation(Location location) {…}
  public static Location getLocation() {…}
}

In Java app that uses the SDK, my customers can use these as if it were a singleton, e.g.

Location currentLocation = MyDevice.getLocation();

When this SDK is integrated into a Kotlin app, it would be natural to express the same as a property:

val currentLocation = MyDevice.location

The problem is, this built-in interop works for non-static methods only.

I can create a singleton in Kotlin and have it handle the translation:

object myDevice {
    var location: Location
    get() = MyDevice.getLocation()
    set(location) = MyDevice.setLocation(location)
}

But won't this single Kotlin file in an otherwise Java-only SDK negatively affect the customers who don't use Kotlin? Can the same be expressed in Java?

Or maybe I should simply convert MyDevice.java to Kotlin? What will be negative effects of such step for the customers who are still on Java?

4

There are 4 answers

0
Alex Cohn On BEST ANSWER

Having read the answers of the experts, having studied the links they provided, and having decompiled the Kotlin code of the wrapper class and analyzed a demo app (pure Java) which used the Kotlin-wrapped library, I decided to change the Java API.

Now I have a class with a public static object:

public class MyDevice { 
  public static MyDevice myDevice;
  public void setLocation(Location location) {…}
  public Location getLocation() {…}
}

now the Java consumers will use

import static com.example.sdk.MyDevice.myDevice;

Kotlin consumers don't need that static:

import com.example.sdk.MyDevice.myDevice

So, I don't need a separate Kotlin flavor of my library!

1
92AlanC On

In Kotlin, in order to achieve the same result as static methods in Java interfaces you should use a companion object to declare your static methods. Example:

interface MyDevice {

    // instance methods   

    companion object {
        // static methods

        @JvmStatic
        fun setLocation(location: Location) {...}

        @JvmStatic
        fun getLocation(): Location {...}
    }
}

Note the @JvmStatic annotation. When you're calling those Kotlin functions from a Java class they will be interpreted as static methods. Example:

public void myJavaMethod() {
    Location location = MyDevice.getLocation();
}
3
Sergei Rybalkin On

The problem you described is a lack of meta data Kotlin need to treat static methods as Class extension properties of extension functions.

Together with issue KT-11968 it makes it not possible for now, at least.

The best possible option is API conversion to Kotlin with a support of @JvmStaic/@JvmField and @JvmDefault where necessary for backward compatibility.

interface MyDevice {
  companion object {
    // nullable type or explicit init
    var location: Location? 
      @JvmStatic get
      @JvmStatic set


// kotlin
val x = MyDevice.location
MyDevice.location = x


// java
var x = MyDevice.getLocation();
MyDevice.setLocation(x);
0
borrom On

There are few solution provided by kotlin lang, there we can use to make it simple with your case. It is the nature of kotlin to make better work between Java, for me I didn't see any drawback of using Companion/Object to create the static method similar to Java. The kotlin language itself also provide many convenient helper for developer for the simplicity. As below what we can apply:

  1. Object
    object MyDevice {
    
        @JvmStatic
        fun getLocation(): Location {
        }
    
        @JvmStatic
        fun setLocation(location: Location) {
    
        }
    }
  1. Companion
    class MyDevice {
        companion object {
    
            @JvmStatic
            fun getLocation(): Location {
    
            }
    
            @JvmStatic
            fun setLocation(location: Location) {
    
            }
        }
    }

Call in Java:

MyDevice.setLocation(location);
final Location location = MyDevice.getLocation();