eclipse-ee4j / jersey

Eclipse Jersey Project - Read our Wiki:
https://github.com/eclipse-ee4j/jersey/wiki
Other
686 stars 341 forks source link

Kotlin/Java-interop issue #4105

Open hosswald opened 5 years ago

hosswald commented 5 years ago

This issue has been raised in the Kotlin issue tracker as well: https://youtrack.jetbrains.com/issue/KT-30925 For a detailed description and a zipped code example please review that ticket. I'm not sure if the problem is bad Kotlin Bytecode or a bug in Jersey's method scanner. Short abstract of the problem: This java interface

@Path("/")
public interface Interface {
    @GET
    String myOperation(Boolean myFlag);
}

together with this Kotlin implementation:

class BrokenImplementation : Interface {
    override fun myOperation(myFlag: Boolean) = "Hello World"
}

will compile, but lead to 404 errors. If I change the parameter to myFlag: Boolean?, it works.

max-kammerer commented 5 years ago

Please note that Kotlin compiler generates additional bridge method for override fun myOperation(myFlag: Boolean) and doesn't generate it for override fun myOperation(myFlag: Boolean?)

Decompiled class from KT-30925

public final class BrokenImplementation implements Interface {
   @NotNull
   public String myOperation(boolean myFlag) {
      return "Hello World";
   }
   // $FF: synthetic method
   // $FF: bridge method
   public String myOperation(Boolean var1) {
      return this.myOperation(var1);
   }
}
jansupol commented 5 years ago

An application server/wadl should return a list of available endpoints, this could tell why 404 was returned.

danielkec commented 4 years ago

Hi @max-kammerer, its seems working properly, as you said nullable Boolean(kotlin.Boolean?) is compiled as standard Java java.lang.Boolean, but non-nullable kotlin.Boolean is compiled as primitive boolean with additional bridge method. If you change your interface like this:

    @GET
    String myOperation(@PathParam("myFlag") boolean myFlag);

Your BrokenTest should work and your GoodImplementation won't be compilable. I would expect your original sample to be also uncompilable in Kontlin because this is valid in Kotlin:

interface Interface2 {
    fun myOperation(myFlag:Boolean);
    fun myOperation(myFlag:Boolean?);
}

I am no expert in Kotlin-Java interop, but it looks like a Kotlin bug, what do you think?

danielkec commented 4 years ago

I have got even better example:

//Java interface
public interface Interface {
    String myOperation(boolean myFlag);
    String myOperation(Boolean myFlag);
}
//Kotlin interface
interface Interface2 {
    fun myOperation(myFlag:Boolean): String;
    fun myOperation(myFlag:Boolean?): String;
}
//This is compilable
class GoodImplementation : Interface2 {
    override fun myOperation(myFlag: Boolean?): String = "Hello World"
    override fun myOperation(myFlag: Boolean): String = "Hello World"
}
// But this ...
class BrokenImplementation : Interface {
    override fun myOperation(myFlag: Boolean?): String = "Hello World"
    override fun myOperation(myFlag: Boolean): String = "Hello World"
}

Compile time error caused by syntetic method:

Platform declaration clash: The following declarations have the same JVM signature (myOperation(Ljava/lang/Boolean;)Ljava/lang/String;):
    fun myOperation(myFlag: Boolean): String defined in BrokenImplementation
    fun myOperation(myFlag: Boolean?): String defined in BrokenImplementation