Call Kotlin object with class delegation from Java as a static method
Asked Answered
C

2

21

This may be a bit difficult to describe, so I'll try to give a concrete example of what I'm trying to do.

Suppose we have a Facade interface and class (in Java), like this:

interface FacadeInterface<T> {
    void method(String from, String via);
}

class Facade<T> implements FacadeInterface<T> {
    private Class<T> mClazz;

    public Facade(Class<T> clazz) {
        mClazz = clazz;
    }

    @Override
    public void method(String from, String via) {
        System.out.println("Method called from " + from + " via " + via);
    }
}

In my applications, I need to have multiple singletons which hold an instance of the facade. The real facade has additional setup/config parameters but those are irrelevant here.

Before I started using kotlin, I would have a class which holds a static instance of the facade (not really a singleton, but in my case, it served a similar purpose) which proxied the calls to the facade, like this:

public class Singleton {
    private static final FacadeInterface<String> sFacade = new Facade<>(String.class);

    private Singleton() {
    }

    public static void method(String from, String via) {
        sFacade.method(from, via);
    }
}

Now, with Kotlin we have class delegates which allow me to write something like this:

object SingletonKt : FacadeInterface<String> by Facade(String::class.java)

This is great - no more boilerplate and I can call SingletonKt from Kotlin classes the same way I called the java Singleton:

Singleton.method("Kotlin", "Singleton")
SingletonKt.method("Kotlin", "SingletonKt")

But, a slight problem arises when I use SingletonKt from Java. Then I have to specify INSTANCE:

Singleton.method("Java", "Singleton");
SingletonKt.INSTANCE.method("Java", "SingletonKt");

I am aware of the @JvmStatic annotation, but the only place I can put it in the SingletonKt file without causing compile errors is right before FacadeInterface and it doesn't seem to do the trick.

Is there a way to set up this class delegate so that I can call it from Java as if it were a static method, without introducing the boilerplate of creating proxy methods for SingletonKt (which would defeat the purpose of the class delegate)?

Comment answered 9/6, 2017 at 9:34 Comment(1)
Probably something, you could report as an issue to jetbrains on kotlin youtrack.Brunner
A
8

It's sadly not possilble!

The Kotlin Delegation is a nice way to reduce boilerplate code. But it comes with the inability to actually access the delegate within the class body.

The second issue you're facing regarding @JvmStatic is actually more drastic to your cause than the first and also applies to you when implementing the delegation manually:

Override members cannot be '@JvmStatic' in object

So instead of exposing the method() through the INSTANCE only, you could delegate it to a staticMethod() on the object. This still differs from your intent, but comes close to it.

object SingletonKt : FacadeInterface<String> by Facade(String::class.java)
    @JvmStatic fun staticMethod(from: String, via: String) = method(from, to)
}
Aquitaine answered 16/5, 2018 at 10:25 Comment(2)
Since this is the only answer so far to explicitly answer (even though it states it's not possible) the question I'm going to accept it. If the original question becomes possible in the future and someone provides the answer I'm willing to change the accepted answer - for now it's either using INSTANCE or one of the suggested workarounds.Comment
I'm disappointed at the state of things too. Unfortunately, bounties are permanent once assigned, and I don't feel good about awarding the bounty in this case. :-( (But, because the OP accepted your answer, you'll still be auto-awarded the bounty anyway. So don't fret! :-))Brost
H
2

I don't know if it is possible to have delegated methods as static methods inside an object in Kotlin.

However, as you are interested in creating singletons that proxy a class, you could use package-level constants in Kotlin:

val SingletonKt : FacadeInterface<String> = Facade(String::class.java)

Now, you can call SingletonKt.method just like you would in Java. Note that you need to use a static import in Java to be able to use the SingletonKt constant.

This also allows you to use features like lazy to only create the singleton (or, in this case, instance) when you need it.

Hystero answered 12/5, 2018 at 16:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.