Question
How can I deserialize JSON stored in pref.turns into a List<Turns> using Gson in Kotlin?
I tried the following code:
val turnsType = TypeToken<List<Turns>>() {}.type
val turns = Gson().fromJson(pref.turns, turnsType)
However, Kotlin reports this error:
Cannot access '<init>': it is public /*package*/ in TypeToken
What is the correct Kotlin syntax for using Gson TypeToken with a generic list?
Short Answer
You will learn why Gson needs a TypeToken for generic types such as List<Turns>, why Java-style anonymous-class syntax does not work directly in Kotlin, and how to deserialize generic JSON safely.
Concept
Gson can deserialize a simple, concrete type directly:
val turn = Gson().fromJson(json, Turns::class.java)
A generic type such as List<Turns> is different. At runtime, the JVM often knows only that the value is a List; the Turns part may no longer be available. This is called type erasure.
If Gson receives only List::class.java, it can create a list but does not reliably know which object type belongs inside it. Gson's TypeToken captures the complete type information, including the element type:
List<Turns>
In Java, developers usually create an anonymous subclass with new TypeToken<List<Turns>>() {}. Kotlin uses a different syntax for anonymous objects: object : SomeType() {}. The error occurs because TypeToken<List<Turns>>() {} tries to call the constructor directly instead of creating the required subclass.
Mental Model
Think of List as a box with a label that says only list. Gson can see that it should make a box, but it needs another label telling it what belongs inside: Turns.
TypeToken<List<Turns>> is that detailed shipping label. It tells Gson:
- create a list;
- read every JSON object in the list;
- turn each object into a
Turnsinstance.
In Kotlin, object : TypeToken<...>() {} means “create a small, one-time subclass that carries this label.”
Syntax and Examples
Use an anonymous object that extends TypeToken:
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
val turnsType = object : TypeToken<List<Turns>>() {}.type
val turns: List<Turns> = Gson().fromJson(pref.turns, turnsType)
The important part is object ::
object : TypeToken<List<Turns>>() {}
It is Kotlin's equivalent of Java's anonymous subclass syntax.
For example, given this model and JSON:
data class Turns(
val id: Int,
val text: String
)
val json = """
[
{"id": 1, "text": "Hello"},
{"id": 2, "text": "Goodbye"}
]
""".trimIndent()
val type = object : TypeToken<List<Turns>>() {}.type
val parsedTurns: List<Turns> = Gson().fromJson(json, type)
println(parsedTurns[0].text) // Hello
Make sure the import is Gson's public TypeToken class:
import com.google.gson.reflect.TypeToken
Do not import a similarly named class from another package.
Step by Step Execution
Consider this code:
data class Turns(val id: Int, val text: String)
val json = """[{"id": 1, "text": "Move left"}]"""
val type = object : TypeToken<List<Turns>>() {}.type
val turns: List<Turns> = Gson().fromJson(json, type)
Execution happens as follows:
Turnsdefines the shape of one turn: anidandtext.jsoncontains a JSON array, indicated by[and].object : TypeToken<List<Turns>>() {}creates an anonymousTypeTokensubclass..typeretrieves the runtime description ofList<Turns>.fromJsonsees that the top-level JSON value is a list.- Gson reads each array item and creates a
Turnsobject from it. - The result is assigned to
turns, which is a KotlinList<Turns>.
Real World Use Cases
TypeToken is useful whenever your JSON target includes generic type arguments.
- Preferences or local storage: Restore a saved list of
Turns, bookmarks, or recently viewed items. - API responses: Parse a JSON array into
List<Product>,List<Comment>, orList<User>. - Cached data: Read cached search results after an app restart.
- Nested collections: Parse values such as
Map<String, List<Turns>>. - Generic wrappers: Parse API envelopes such as
ApiResponse<List<Turns>>when the response model itself is generic.
Real Codebase Usage
In production code, avoid repeating object : TypeToken<...>() {}.type throughout the project. A common Kotlin pattern is an inline function with a reified type parameter:
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
inline fun <reified T> Gson.fromJsonTyped(json: String): T {
val type = object : TypeToken<T>() {}.type
return fromJson(json, type)
}
Call it with the target type at the call site:
val turns: List<Turns> = Gson().fromJsonTyped(pref.turns)
Real applications should also handle missing or invalid persisted JSON. For example, return an empty list when the stored value is absent:
fun loadTurns(json: String?): List<Turns> {
if (json.isNullOrBlank()) return emptyList()
val type = object : TypeToken<List<Turns>>() {}.type
return Gson().fromJson(json, type)
}
For untrusted input, wrap parsing in error handling because malformed JSON can throw a parsing exception.
Common Mistakes
Using Java syntax directly in Kotlin
This does not create an anonymous subclass in Kotlin:
// Incorrect
val type = TypeToken<List<Turns>>() {}.type
Use Kotlin's object expression:
// Correct
val type = object : TypeToken<List<Turns>>() {}.type
Passing only List::class.java
// Avoid for a list of custom objects
val turns = Gson().fromJson(json, List::class.java)
The generic element type is missing. Gson may produce values that behave like generic maps instead of Turns objects. Use a TypeToken<List<Turns>>.
Using the wrong import
Verify that the file imports:
import com.google.gson.reflect.TypeToken
An incorrect TypeToken import can cause confusing constructor or type errors.
Assuming stored JSON is always valid
A preference value can be missing, blank, or corrupted. Check for blank values and decide on a fallback, often emptyList().
Forgetting that Kotlin non-null properties need JSON values
If a JSON field is absent while the Kotlin model expects a non-null value, the resulting object may contain unexpected defaults or later fail when used. Ensure your model and JSON format agree, or provide sensible defaults in the data class.
Comparisons
| Approach | Best for | Keeps generic type information? |
|---|---|---|
Turns::class.java | One non-generic object | Not needed |
List::class.java | Rare cases where item type does not matter | No |
object : TypeToken<List<Turns>>() {}.type | Lists, maps, and nested generic types | Yes |
| Reified Gson helper | Repeated parsing in Kotlin codebases | Yes |
TypeToken is not required for every Gson call. It is mainly required when the target type contains generics, such as List<Turns>, Map<String, Turns>, or ApiResponse<Turns>.
Cheat Sheet
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
Parse one object
val turn: Turns = Gson().fromJson(json, Turns::class.java)
Parse a generic list
val type = object : TypeToken<List<Turns>>() {}.type
val turns: List<Turns> = Gson().fromJson(json, type)
Parse a generic map
val type = object : TypeToken<Map<String, Turns>>() {}.type
val turnsById: Map<String, Turns> = Gson().fromJson(json, type)
Reusable Kotlin helper
inline fun <reified T> Gson.fromJsonTyped(json: String): T =
fromJson(json, object : TypeToken<T>() {}.type)
Key rule
Use object : TypeToken<T>() {} in Kotlin—not TypeToken<T>() {}.
FAQ
Why does Gson need TypeToken for List<Turns>?
The runtime type List does not include its item type reliably because of JVM type erasure. TypeToken preserves the Turns information for Gson.
What is the Kotlin equivalent of new TypeToken<List<Turns>>() {}?
Use:
object : TypeToken<List<Turns>>() {}
Can I use List::class.java instead of TypeToken?
Not when you need Gson to create Turns objects inside the list. List::class.java does not describe the element type.
Which TypeToken should I import?
Use:
import com.google.gson.reflect.TypeToken
Does TypeToken work for nested generics?
Yes. For example:
val type = object : TypeToken<Map<String, List<Turns>>>() {}.type
Can I avoid writing TypeToken repeatedly?
Yes. Create an inline reified helper around Gson's fromJson method and call it with an explicit target type.
What should happen when the saved JSON is blank?
For a stored list, returning emptyList() is often appropriate. Check json.isNullOrBlank() before parsing.
Mini Project
Description
Build a small preference-style loader that converts saved JSON into a list of turn records. This demonstrates how an app can restore structured local data while handling an empty stored value.
Goal
Create a function that returns a List<Turns> from JSON and returns an empty list when no JSON has been saved.
Requirements
Define a Turns data class with an ID and message.
Create a function that accepts nullable JSON text.
Return an empty list for null or blank input.
Use Gson TypeToken to deserialize a List<Turns>.
Demonstrate the function with sample JSON.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.