Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin @JvmStatic and accidental override in a companion object

Tags:

kotlin

swing

I'm working on a Swing look&feel using kotlin. In order to create a UI, Swing requires to have a static method createUI with the following signature:

class ButtonUI: BasicButtonUI() {
    ...
    companion object {
        @JvmStatic fun createUI(p0: JComponent): ComponentUI {
           ...
        }
    }
}

and then it is called via reflection in Swing code:

m = uiClass.getMethod("createUI", new Class[]{JComponent.class});

Unfortunately, the code above cannot be compiled by the kotlin compiler because of:

Error:(88, 9) Kotlin: Accidental override: The following declarations have the same JVM signature (createUI(Ljavax/swing/JComponent;)Ljavax/swing/plaf/ComponentUI;):
    fun createUI(c: JComponent): ComponentUI
    fun createUI(p0: JComponent!): ComponentUI!

Is there a workaround for this case?

like image 632
dimafeng Avatar asked Jun 17 '17 16:06

dimafeng


1 Answers

it's a kotlin bug KT-12993. Unfortunately, the bug is not fixed yet. just using java implements your ButtonUI or switch between java and kotlin to solving the problem if you want to let kotlin implements your ui logic. for example, you should define a peer between java and kotlin.

the java code as below:

public class ButtonUI extends BasicButtonUI {
    private ButtonUIPeer peer;

    public ButtonUI(ButtonUIPeer peer) {
        this.peer = peer;
    }

    @Override
    public void installUI(JComponent c) {
        peer.installUI(c, () -> super.installUI(c));
    }

    // override other methods ...


    public static ComponentUI createUI(JComponent c) {
        // create the peer which write by kotlin 
        //                        |
        return new ButtonUI(new YourButtonUIPeer());
    }
}


interface ButtonUIPeer {
    void installUI(Component c, Runnable parentCall);
    //adding other methods for the ButtonUI
}

the kotlin code as below:

class YourButtonUIPeer : ButtonUIPeer {
    override fun installUI(c: Component, parentCall: Runnable) {
      // todo: implements your own ui logic
    }
}

IF you have more than half dozen methods to implements, you can using the Proxy Design Pattern just delegate request to the target ButtonUI which implemented in kotlin (many IDE support generates delegate methods for a field). for example:

public class ButtonUIProxy extends BasicButtonUI {
    private final BasicButtonUI target;
    //1. move the cursor to here ---^
    //2. press `ALT+INSERT`
    //3. choose `Delegate Methods` 
    //4. select all public methods and then click `OK`

    public ButtonUIProxy(BasicButtonUI target) {
        this.target = target;
    }

    public static ComponentUI createUI(JComponent c){
        // class created by kotlin     ---v
        return new ButtonUIProxy(new ButtonUI());
    }
}
like image 91
holi-java Avatar answered Jan 17 '23 21:01

holi-java