I have a few interfaces that looks something like the following:
interface ApiInterface : Context.Element {
@GET(Urls.url)
suspend fun getSomeData(): Data
}
interface Context.Element {
operator fun plus(context: Context): Context
}
I am trying to implement a Context
implementation in similar way of the Kotlin CoroutineContext
. As I am trying to add my Retrofit http interface as a Context.Element
it ends up inheriting some other functions. The code compiles and runs fine until I call a function from my ApiInterface instance which inherits from Context.Element
therefore doesn't have any @GET
, @POST
or any retrofit annotation.
If I run the following code:
val context = ApiInterfaceImpl()
context + DifferentApiInterfaceImpl()
I receive a Exception in thread "main" java.lang.IllegalArgumentException: HTTP method annotation is required (e.g., @GET, @POST, etc.). for method Context.plus
.
I believe that if I am able to make the Retrofit annotation processor to skip the functions inherited from Context.Element
this problem would be solved. Is there any way to to this? Any @Transient
like annotation for functions? I tried to use @JvmSynthetic
but no luck.
The issue here is not in annotation processor, but in the way Retrofit works. It doesn't generate an implementation class, but creates a proxy object at runtime and this proxy object handles all method calls and routes them to the proper handler. So obviously it can't handle a non-service method. Also it's not clear how you would provide an implementation for that non-annotated method. So I would say it's currently impossible.
Edit:
As you are using default method on interface to provide the implementation, it should generally work I believe, because Retrofit
handles default methods separately. The issue here might be that Retrofit
knows about java default methods only and by default kotlin does not use them (for compatibility with java 7 and below). So if you make the compiler generate java default method, it should work. Please check out this post for details.
I see. Thanks, food to know... I always thought it was backed by a generated implementation class. By the way, the way I provided the implementation for the non-annotated method (the
plus()
) was just by having a default implementation in the interface. The problem of not being able to make the retrofit interface extend myContext
interface is that I'm not able to have the sameKey
for theApiInterface
implementation present in myContext
. Trying to figure out a nice way to do that. Thanks again for your help!@admqueiroga Please check out the updated answer.
I think that's exactly what I need. Trying to add the
-Xjvm-default=enable
to the Kotlin project but now I have to deal withError: Could not create the Java Virtual Machine.
I guess once I figure out where to add the argument it will work. Right now I am adding likekotlinOptions.freeCompilerArgs += listOf("-Xjvm-default=enable")
but it's breaking the JVM. Thanks again for the help @esentsov! Really appreciate!If I don't set
kotlinOptions.freeCompilerArgs += listOf("-Xjvm-default=enable")
the IDE displays an error wherever I am using@JvmDefault
. When I do set this flag the errors disappears but the JVM can't be created due toUnrecognized option: -Xjvm-default
. Using Gradle by the wayHave you also set
jvmTarget = "1.8"
for kotlin andsourceCompatibility
,targetCompatibility
toJavaVersion.VERSION_1_8
?