skip to content

What does @Transient do in kotlinx.serialization, and why must such a property have a default value?

level: middleimportance: must knowfreq 65%

answer

  1. Skips the property from the descriptor entirely
  2. Must have a default — plugin enforces it at compile time
  3. kotlinx.serialization.Transient, not the JVM one
  4. Never encoded, never decoded
  5. For caches/derived/secret fields

basics

~20 s

@Transient tells the serializer to skip a property entirely: it is not written to JSON and not read from it. Because it is never decoded, it must have a default value so an object can still be constructed.

solid answer

~40 s

kotlinx.serialization's @Transient (from kotlinx.serialization, NOT java.beans.Transient) excludes a property from the serial descriptor. It is never encoded and never decoded — its serial form simply doesn't exist. Therefore it must declare a default value (= ...), because during decoding the serializer has no source for it and must initialize it from the default. The compiler plugin enforces this: a @Transient property without a default is a compile error. Typical uses: derived/computed state, caches, lazily-built fields, or values you never want on the wire (secrets). It differs from making a property non-@Serializable: @Transient is the explicit, intentional 'skip me' marker. Note it only affects properties declared in the body or constructor of a @Serializable class; the plugin only serializes properties anyway, and @Transient additionally drops constructor properties from the descriptor.

code

kotlin · 8 lines
kotlin
@Serializable
data class Cart(
    val items: List<String>,
    @Transient val computedTotalCache: Int = 0, // skipped, needs default
)

Json.encodeToString(Cart(listOf("a")))
// {"items":["a"]}

go deeper

for a junior

Knows @Transient hides a property from JSON.

for a middle

Explains the mandatory default and that it is excluded from both encode and decode across all formats.

for a senior

Distinguishes it from backing-field-less properties, contrasts with @Required, and cites the plugin's compile-time enforcement.

for a principal

Discusses modeling derived/secret state, payload-contract implications, and the danger of confusing it with JVM transient in mixed serialization stacks.

## What @Transient means here `@Transient` (the one in `kotlinx.serialization`, **not** `java.io.Serializable`/`@java.beans.Transient`) marks a property of a `@Serializable` class as **excluded from serialization**. It is left out of the **serial descriptor**, so: - it is **never written** during encoding, and - it is **never read** during decoding (the encoded form contains nothing for it). ```kotlin import kotlinx.serialization.* @Serializable data class Session( val token: String, @Transient val decodedAt: Long = System.currentTimeMillis(), // skipped ) ``` Encoding `Session("abc")` yields `{"token":"abc"}` — `decodedAt` is absent. ## Why a default is mandatory When decoding, the serializer reconstructs the object but has **no value** for a transient property (nothing was on the wire). It therefore needs a value to put there. Kotlin's answer is the property's **default value**. The serialization **compiler plugin enforces** this: ```kotlin @Serializable class Bad(@Transient val cache: Map<String, Int>) // COMPILE ERROR // "This property is marked as @Transient and therefore must have a default value" ``` Add `= emptyMap()` and it compiles. The default is what every decoded instance receives. ## When to use it - **Derived/computed state** you can recompute (`val fullName get() = ...` — though computed `get()`-only properties are already not serialized; `@Transient` is for stored properties). - **Caches / lazies / timestamps** that shouldn't travel. - **Secrets** you must keep out of the payload. ## @Transient vs other ways to skip - A property with **no backing field** (an expression body `val x get() = a + b`) is **not** serialized at all — `@Transient` is unnecessary and actually not allowed there. - `@Transient` is for **stored** properties (constructor params or `val/var` with backing fields) you want to drop. - It is the opposite of `@Required`: `@Transient` removes a property; `@Required` forbids omitting one. ## Gotchas - Do not confuse it with the JVM `transient` keyword or `java.io.Serializable` — kotlinx.serialization has **its own** annotation. - A `@Transient` property cannot also be `@Required` or `@SerialName`-renamed meaningfully — it has no serial presence.

  • What happens if you put @Transient on a property with no default value?
    It is a compile-time error from the serialization plugin: a transient property must have a default.
  • Is an expression-body val (val x get() = a + b) serialized, and should you annotate it @Transient?
    It has no backing field so it is not serialized anyway; @Transient is unnecessary and not applicable there.

Like marking a line in a form 'office use only' — it never appears on the printed copy you exchange, so it always starts from a preset value.

saying these in an interview costs you the question

  • Confusing kotlinx @Transient with java.io transient / @java.beans.Transient
  • Saying a transient property can omit a default value
  • Claiming it is still decoded but just not encoded
  • Putting @Transient on a computed get()-only property
  • Thinking @Transient only hides from JSON but keeps it in CBOR/ProtoBuf

context