Given an interface method like this (Android Retrofit), how do I read the URL path specified in the annotation argument from Kotlin code at runtime?
ApiDefinition interface:
@GET("/api/somepath/objects/")
fun getObjects(...)
Read the annotation value:
val method = ApiDefinition::getObjects.javaMethod
val verb = method!!.annotations[0].annotationClass.simpleName ?: ""
// verb contains "GET" as expected
// But how to get the path specified in the annotation?
val path = method!!.annotations[0].????????
UPDATE 1
Thanks for answers. I'm still struggling as I can't see what type to use to do the following:
val apiMethod = ApiDefinition::getObjects
.... then to pass that function reference into a method like this (it's reused)
private fun getHttpPathFromAnnotation(method: Method?) : String {
val a = method!!.annotations[0].message
}
IntelliJ IDE is suggesting I use KFunction5<> as a function parameter type (it doesn't exist as far as I can see) and seems to be requiring I specify all the parameter types for the method too, which makes a generic call to get the annotation attribute impossible. Isn't there a Kotlin equivalent of "Method"?, a type that will accept any method? I tried KFunction, without success.
UPDATE 2
Thanks for clarifying things. I've got to this point:
ApiDefinition (Retrofit interface)
@GET(API_ENDPOINT_LOCATIONS)
fun getLocations(@Header(API_HEADER_TIMESTAMP) timestamp: String,
@Header(API_HEADER_SIGNATURE) encryptedSignature: String,
@Header(API_HEADER_TOKEN) token: String,
@Header(API_HEADER_USERNAME) username: String
): Call<List<Location>>
Method to retrieve annotation argument:
private fun <T> getHttpPathFromAnnotation(method: KFunction<T>) : String {
return method.annotations.filterIsInstance<GET>().get(0).value
}
Call to get the path argument for a specific method:
val path = getHttpPathFromAnnotation<ApiDefinition>(ApiDefinition::getLocations as KFunction<ApiDefinition>)
The implicit cast seems to be necessary or the type parameter demands I provide a KFunction5 type.
This code works, but it has the GET annotation hard-coded, is there a way to make it more generic? I suspect I might need to look for GET, POST and PUT and return the first match.
Use the Kotlin KFunction
directly instead of javaMethod
(you're using Kotlin anyway!), and findAnnotation
for concise, idiomatic code.
This will also work if the annotation happens to not be the first, where annotations[0]
may break.
val method = ApiDefinition::getObjects
val annotation = method.findAnnotation<GET>() // Will be null if it doesn't exist
val path = annotation?.path
Basically all findAnnotation
does is return
annotations.filterIsInstance<T>().firstOrNull()