Show how the inline+reified pattern turns Gson's `fromJson(json, Class)` into `fromJson<T>(json)`. What gets generated at the call site?
answer
- reified T -> T::class.java token
- inline fun <reified T> Gson.fromJson
- call site emits fromJson(json, User::class.java)
- nested generics need TypeToken
- removes the explicit Class argument
basics
~20 sYou write a small inline extension with a reified type. Inside it you can use T::class.java, so you call the old fromJson(json, T::class.java) for the caller. At the call site T is replaced by the real class, so the user just writes fromJson<User>(json).
solid answer
~40 sGson's raw API is `fun <T> fromJson(json: String, classOfT: Class<T>): T`, forcing callers to pass `User::class.java`. Because a normal generic can't recover its `Class` at runtime (erasure), the ergonomic wrapper must be `inline fun <reified T> Gson.fromJson(json: String): T = fromJson(json, T::class.java)`. `reified` lets the body reference `T::class.java`; `inline` makes that legal by copying the body into each call site, where `T` is the concrete type. So `gson.fromJson<User>(json)` inlines to `gson.fromJson(json, User::class.java)` — the explicit class token is generated for you. For generic targets like `List<User>`, `T::class.java` loses the inner type, so you still need a `TypeToken<List<User>>(){}.type`; reified alone can't capture nested generic arguments through erasure.
code
kotlin · 9 linesinline fun <reified T> Gson.fromJson(json: String): T =
fromJson(json, T::class.java)
// nested generics: T::class.java loses the element type, use TypeToken
inline fun <reified T> Gson.fromJsonTyped(json: String): T =
fromJson(json, object : TypeToken<T>() {}.type)
val user: User = gson.fromJson(json)
val users: List<User> = gson.fromJsonTyped(json)go deeper
Writes the basic wrapper and shows fromJson<User>(json) works because T::class.java is available.
Explains the call-site expansion to fromJson(json, User::class.java) and that inline is what makes T::class.java legal.
Knows the nested-generic limitation and reaches for TypeToken/anonymous subclass to capture parameterized types.
Generalizes the idiom across Retrofit/Koin/serialization libraries and weighs ergonomics vs. inlined bytecode growth on a public API.
## The boilerplate this pattern removes Gson exposes: ```kotlin // java: <T> T fromJson(String json, Class<T> classOfT) ``` In Kotlin you'd call it as `gson.fromJson(json, User::class.java)`. The `User::class.java` is pure noise the caller is forced to repeat. We can't hide it in an ordinary generic function because at runtime `T` is **erased** — `fun <T> parse(json: String): T = gson.fromJson(json, /* no Class available */)` won't compile. ## The reified wrapper ```kotlin inline fun <reified T> Gson.fromJson(json: String): T = fromJson(json, T::class.java) // usage val user: User = gson.fromJson(json) // T inferred from the variable type val user2 = gson.fromJson<User>(json) // T given explicitly ``` - `reified T` lets the body mention `T::class.java` (a `Class<T>` token). - `inline` copies the body to the call site so `T` becomes the concrete class there. ## What the compiler actually emits After inlining, `gson.fromJson<User>(json)` becomes effectively: ```kotlin gson.fromJson(json, User::class.java) ``` The class token you *would* have typed by hand is synthesized from the reified `T`. No reflection cost beyond the normal `User::class.java` lookup. ## Limitation: nested generics `T::class.java` only carries the **raw** class. For `List<User>` it yields `List` and the element type is lost to erasure. Gson handles this with an anonymous `TypeToken` subclass that captures the full type via the class signature: ```kotlin inline fun <reified T> Gson.fromJsonTyped(json: String): T = fromJson(json, object : TypeToken<T>() {}.type) ``` Here the *reified* `T` is baked into the synthesized anonymous class at the call site, and `TypeToken` reads `getGenericSuperclass()` to recover `List<User>`. This is the standard idiom for collection/parameterized payloads. ## Why this is the textbook example It shows the whole value proposition: reified turns a type *argument* into a usable runtime *value* (`Class`/`KClass`), eliminating a class-literal parameter and improving type inference — the same trick underlies Retrofit, Koin `inject<T>()`, Android `intent<Activity>()`, etc.
- Why does `fromJson<List<User>>(json)` using `T::class.java` lose the element type?`T::class.java` returns only the raw `List` class; the `User` argument is erased. You need `TypeToken<List<User>>` which captures the parameterized type via the anonymous class's generic superclass.
- Could you write this wrapper non-inline by adding a `Class<T>` parameter?Yes, but then the caller passes the class again — you've lost the whole ergonomic win. The point of inline+reified is to synthesize that token automatically.
saying these in an interview costs you the question
- Claiming reified captures nested generic arguments like List<User> (it doesn't)
- Forgetting to mark the function inline so T::class.java won't compile
- Saying the wrapper adds reflection overhead beyond a normal class literal
- Thinking T::class.java requires passing a Class parameter anyway