2

I've a problem using Fuel's responseObject in a generic fashion. I'm trying to develop a centralized method with components getting their HTTP response object already deserialized, ready to go. It looks like this:

class Controller(private val url: String) {
  fun <T> call(endpoint: String): T {
    return "$url/$endpoint".httpGet().responseObject<T>()
  }
}
class App(private val controller: Controller) {
  fun getModel() { 
    val model = controller.call<AppModel>("model")
    // use model
  }
}

Of course, Controller.call would handle errors, and add common request parameters. The deserialization from JSON is supposed to be handled by Jackson (AppModel is a simple data class Jackson should pick up automatically), so I'm working with fuel-jackson:1.12.0 as an added dependency.

Now, using Kotlin-1.2.21, I get this compiler error:

Error:(35, 97) Kotlin: Cannot use 'T' as reified type parameter. Use a class instead.

How do I work around this, perhaps by switching to a different Fuel method?

I've considered making call inline (to reify T), but this defeats the purpose of having a private val url.

mabi
  • 5,279
  • 2
  • 43
  • 78

1 Answers1

3

I don't think there's a simple workaround to this problem.

First, there's no way to call a Kotlin inline function with a reified type parameter without either using a concrete type or propagating the type argument through a chain of generic calls to inline functions, so you have to call .httpGet().responseObject<T>() from an inline function and use a reified type parameter as T.

Next, there's a reason for the restrictions on what an inline function can access. Basically, allowing inline functions to access non-public API would sometimes break binary compatibility. This is described in the docs here.

What you can do is, as suggested in the docs, make private val url: String a @PublishedApi internal val and, accordingly, go on with inline fun <reified T> call(...).

If you are worried about url becoming effectively public, you might want to take a look at this Q&A suggesting a workaround with @JvmSynthetic.

hotkey
  • 140,743
  • 39
  • 371
  • 326
  • Thanks for the insight and confirming that I can't do this (easily). Would changing the way I interact with Fuel (use a different API, fex) change this? I'm not a super hot fan of the extension method anyway, so if I can use a different approach to Fuel, that would also work. – mabi Feb 02 '18 at 16:39
  • @mabi There are non-inline functions for `Request` that accept a deserializer [(here)](https://github.com/kittinunf/Fuel/blob/65b1bbc93e27f56b17e6c1dd7d732534bd2a0f72/fuel/src/main/kotlin/com/github/kittinunf/fuel/core/Request.kt#L287), and the inline functions call them and provide a proper deserializer (e.g. [here](https://github.com/kittinunf/Fuel/blob/65b1bbc93e27f56b17e6c1dd7d732534bd2a0f72/fuel/src/main/kotlin/com/github/kittinunf/fuel/core/Request.kt#L287)). You can try to do it that way and provide a deserializer, but I don't know how exactly your code structure will allow you to do it – hotkey Feb 02 '18 at 17:10
  • I clarified I'm using Jackson for deserialization. Guess I'll have a look at how to trigger that directly instead of relying on magic. – mabi Feb 02 '18 at 19:46