3

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?

Izak
  • 702
  • 6
  • 23
Alex Cohn
  • 52,705
  • 8
  • 94
  • 269

4 Answers4

2

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);
Sergei Rybalkin
  • 2,890
  • 11
  • 24
  • This is an interesting link, and yes, I lean towards API conversion to Kotlin. The question remains, what are the dangers, or dark sides, of such conversion? Will it not introduce unnecessary dependencies to my library? Increase the size? Require higher **minSdkVersion**? – Alex Cohn Dec 03 '20 at 22:07
  • Apk size - potentially yes, you’d better check the difference after conversion, as companion object is an extra allocation and class/methods declaration in your Dex file. Check how R8 or Redex shrink your Apk if you’re using any of them. It’s unlikely you will need a minSdk bump, as long as you use Kotlin with jvmTarget 1.6. As for dependencies it depends what your consider as unnecessary, someone could say kotlin-stdlib is an unnecessary dep, but it’s unlikely you will have extra deps. – Sergei Rybalkin Dec 03 '20 at 22:15
  • Edited with java and kotlin use cases. Check out generated bytecode for MyDevice – Sergei Rybalkin Dec 06 '20 at 23:07
1

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();
}
92AlanC
  • 1,115
  • 2
  • 10
  • 27
  • Singleton `object` suits me better, see https://kotlinlang.org/docs/reference/object-declarations.html#object-declarations. As for @JvmStatic, you are correct, it should be used if I decide to convert **MyDevice.java** to Kotlin. But even then, with a property explicitly defined in Kotlin, the code looks cleaner, and the Java access is just the same. – Alex Cohn Dec 01 '20 at 08:32
1

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();
borrom
  • 371
  • 1
  • 4
  • 15
0

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!

Alex Cohn
  • 52,705
  • 8
  • 94
  • 269