How to use TypeToken + generics with Gson in Kotlin
Create this inline fun:
inline fun <reified T> Gson.fromJson(json: String) = fromJson<T>(json, object: TypeToken<T>() {}.type)
and then you can call it in this way:
val turns = Gson().fromJson<Turns>(pref.turns)
// or
val turns: Turns = Gson().fromJson(pref.turns)
Previous Alternatives:
ALTERNATIVE 1:
val turnsType = object : TypeToken<List<Turns>>() {}.type
val turns = Gson().fromJson<List<Turns>>(pref.turns, turnsType)
You have to put object :
and the specific type in fromJson<List<Turns>>
ALTERNATIVE 2:
As @cypressious mention it can be achieved also in this way:
inline fun <reified T> genericType() = object: TypeToken<T>() {}.type
use as:
val turnsType = genericType<List<Turns>>()
Another option (not sure it looks more elegant than the others) might be a call like this:
turns = Gson().fromJson(stringObject, Array<Turns>::class.java).toMutableList()
So you are using the java Array class one liner instead of "pure Kotlin".
This solves the problem:
val turnsType = object : TypeToken<List<Turns>>() {}.type
val turns = Gson().fromJson<List<Turns>>(pref.turns, turnsType)
The first line creates an object expression that descends from TypeToken
and then gets the Java Type
from that. Then the Gson().fromJson
method either needs the type specified for the result of the function (which should match the TypeToken
created). Two versions of this work, as above or:
val turns: List<Turns> = Gson().fromJson(pref.turns, turnsType)
To make it easier to create the TypeToken
you can create a helper function, which is required to be inline so that it can use reified type parameters:
inline fun <reified T> genericType() = object: TypeToken<T>() {}.type
Which can then be used in either of these ways:
val turnsType = genericType<List<Turns>>()
// or
val turnsType: List<Turns> = genericType()
And the whole process can be wrapped into an extension function for the Gson
instance:
inline fun <reified T> Gson.fromJson(json: String) = this.fromJson<T>(json, object: TypeToken<T>() {}.type)
So that you can just call Gson and not worry about the TypeToken
at all:
val turns = Gson().fromJson<Turns>(pref.turns)
// or
val turns: Turns = Gson().fromJson(pref.turns)
Here Kotlin is using type inference from one side of the assignment or the other, and reified generics for an inline function to pass through the full type (without erasure), and using that to construct a TypeToken
and also make the call to Gson