
Kotlin object is the standard way to declare a singleton — a single, globally accessible instance created once per JVM.
This guarantee holds at the language level. But in real-world projects, it can break — without compiler errors or visible warnings.
One common cause is serialization. Some libraries return a new instance during deserialization, breaking reference equality and shared state.
This article explains when Kotlin singletons stop being singletons — and how to avoid it in practice.
Featured On
How Gson breaks Kotlin object identity
Most developers use object in Kotlin as a language-level singleton. It’s created once, holds global state, and is referenced consistently throughout the app.
But this guarantee only applies within the same classloader — and only if the object is used directly. Runtime tools like serializers can break this behavior without warning.
Here’s what happens when you serialize and deserialize a Kotlin object using Gson:
object MySingleton {
const val NAME: String = "MySingleton"
}
fun main() {
val gson = Gson()
// serialize
val json = gson.toJson(MySingleton)
// deserialize
val deserialized = gson.fromJson(json, MySingleton::class.java)
println("MySingleton before serialization hashCode: ${System.identityHashCode(MySingleton)}")
println("MySingleton after serialization hashCode: ${System.identityHashCode(deserialized)}")
println("Same instance: ${deserialized === MySingleton}")
}
Output:
MySingleton before serialization hashCode: 399534175 MySingleton after serialization hashCode: 428910174 Same instance: false
Even though the original type is object, Gson creates a new instance on deserialization. Reference equality is lost, and any global state held inside the singleton is not preserved.
Why this happens
Gson does not recognize Kotlin’s object. It treats it as a regular class with fields. During deserialization, it uses reflection to create a new instance — even though object was designed to be a singleton.
This is not a bug in Gson. It’s a mismatch between a Kotlin-specific construct and a library that works at the Java class level.
What to remember
Kotlin object guarantees a single instance per classloader — at the language level.
Serialization libraries like Gson treat it as a regular class and create new instances during deserialization.
If identity matters, using object with serialization requires explicit handling.
To preserve singleton behavior, use a custom adapter that always returns the original instance.
How kotlinx.serialization preserves object identity
Kotlin’s official serialization library does recognize object. When annotated with @Serializable, the deserializer knows it’s a singleton — and returns the existing instance.
@Serializable
object MySingleton {
const val NAME: String = "MySingleton"
}
@OptIn(InternalSerializationApi::class)
fun main() {
val json = Json { encodeDefaults = true }
// serialize
val serialized = json.encodeToString(MySingleton)
// deserialize
val deserialized = json.decodeFromString(MySingleton::class.serializer(), serialized)
println("MySingleton before serialization hashCode: ${System.identityHashCode(MySingleton)}")
println("MySingleton after serialization hashCode: ${System.identityHashCode(deserialized)}")
println("Same instance: ${deserialized === MySingleton}")
}
Output:
MySingleton before serialization hashCode: 399534175 MySingleton after serialization hashCode: 399534175 Same instance: true
This behavior is by design. The Kotlin serialization plugin understands object declarations and reuses the existing instance safely.
How Moshi handles Kotlin object
Moshi takes a stricter approach. Instead of creating a new instance, it throws an exception when asked to serialize or deserialize a Kotlin object.
object MySingleton {
const val NAME: String = "MySingleton"
}
fun main() {
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
val adapter = moshi.adapter(MySingleton::class.java)
val json = adapter.toJson(MySingleton)
val deserialized = adapter.fromJson(json)
}
Output:
Exception in thread "main" java.lang.IllegalArgumentException: Cannot serialize object declaration com.example.MySingleton
This behavior prevents accidental duplication. To deserialize a Kotlin object with Moshi, you must write a custom adapter that always returns the original instance.
Job Offers
Summary
Kotlin object works reliably as a singleton — but only at the language level.
With serialization, that guarantee can break.
- Gson creates a new instance on deserialization
- Moshi throws an error and refuses to serialize
- kotlinx.serialization preserves the original instance
If your app relies on identity, reference equality, or shared state — use caution when combining object with third-party serializers.
In Kotlin-first projects, prefer kotlinx.serialization when working with object.
If you found this article helpful
If you found this article helpful, consider leaving a clap — it helps others discover it.
You can also follow me on Medium for more articles about Kotlin, Android development, and practical engineering topics.
You might also like:

Anatolii Frolov
Senior Android Developer
Writing honest, real-world Kotlin & Jetpack Compose insights.
📬 Follow me on Medium
This article was previously published on proandroiddev.com.


