skip to content

Extension Properties

Extension properties look like fields but have no backing field, so they must be computed in a custom getter. Interviewers ask why, and the answer — you cannot add state to a class you do not own — is the whole point.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What is a Kotlin extension property, and how do you declare one (e.g. a `lastIndex` for `String`)?

level: juniorimportance: must knowfreq 60%

answer

  1. val Receiver.name: T get() = ...
  2. this == receiver instance inside accessor
  3. No backing field -> must be computed
  4. var needs get() and set(value)
  5. No initializer allowed

basics

~10 s

An extension property lets you add a property to an existing type without changing its code. You write it like val String.lastIndex with a custom getter that computes the value.

solid answer

~40 s

An extension property adds a property-like accessor to a type you don't own, using the syntax `val Receiver.name: T get() = ...` (or `var` with a `get()`/`set()`). The part before the dot is the receiver type; inside the accessor, `this` refers to the receiver instance. For example `val String.lastIndex: Int get() = length - 1` lets you call `"abc".lastIndex`. They are resolved statically (by the declared/static type of the expression, not runtime type) and must be top-level or in another class — they cannot truly store state, so the value is always computed in the getter rather than read from a field.

code

kotlin · 6 lines
kotlin
val String.lastIndex: Int
    get() = length - 1

fun main() {
    println("abc".lastIndex) // 2
}

go deeper

for a junior

Can declare a simple val Type.x get() = ... and use it like a property.

for a middle

Explains the no-backing-field rule and why an initializer is illegal.

for a senior

Connects static resolution and top-level placement; knows they can't see private members.

for a principal

Frames extension properties as a syntactic convenience over static accessors, weighing API discoverability trade-offs.

## What an extension property is Kotlin lets you add **properties** to a type you don't control (a third-party class, a stdlib type, even your own) without editing or subclassing it. The type you extend is called the **receiver type**; the value the call is made on is the **receiver instance**, available as `this` inside the accessor. ## Syntax ```kotlin val String.lastIndex: Int get() = length - 1 // 'this' is the String; 'length' == this.length var StringBuilder.firstChar: Char get() = this[0] set(value) { this.setCharAt(0, value) } ``` - `val` = read-only (only a `get()`). - `var` = read/write (`get()` **and** `set(value)`). - The text before the dot (`String`, `StringBuilder`) is the receiver type. Usage looks exactly like a real property: `"abc".lastIndex` returns `2`. ## Key constraint: no backing field A normal property can store a value in a hidden **backing field** (`field`). An extension property has **no backing field** because the class it extends is fixed and Kotlin cannot add storage to instances it doesn't own. Therefore: - You **must** provide an explicit `get()` (and `set()` for `var`). - You **cannot** write an initializer like `val String.lastIndex = length - 1` — that would need storage. The compiler rejects it. - The value is always **computed** on each access. ## Where they can live They are declared at the **top level** of a file, or inside a class/object (then they have two receivers). They are **not** members of the extended class, so they don't appear in its API and can't access its `private` members. This stays within "what an extension property is and how to declare it" — function-style extensions, nullable/generic receivers, and resolution rules are separate concerns.

  • Why can't you write `val String.lastIndex: Int = length - 1`?
    An initializer requires a backing field to store the value, but extension properties have no backing field, so you must compute it in `get()` instead.
  • Can an extension property be a `var`?
    Yes, if you provide both a `get()` and a `set(value)`; the setter typically delegates to a mutating method on the receiver.

Like a sticky note clipped onto an object: it tells you something computed about the object, but it isn't stored inside the object itself.

saying these in an interview costs you the question

  • Claiming extension properties store state in the instance
  • Writing an initializer instead of get()/set()
  • Saying they become real members of the extended class
  • Confusing the receiver type with the return type

context

open as a page

Why does an extension property have no backing field, and what does the `field` keyword do (or fail to do) inside one?

level: middleimportance: must knowfreq 50%

basics

~10 s

A backing field is hidden storage attached to an instance. Extensions don't own the class, so they can't add storage. The value must be calculated each time in the getter; you can't use field.

open as a page

Show a correct `var` extension property with a working setter. What can its `set(value)` legally do, given there's no backing field?

level: middleimportance: should knowfreq 30%

basics

~10 s

A mutable extension property's setter must change something that already exists on the receiver — like calling a method that mutates it. It can't store the value itself because there's no field.

open as a page

How does an extension property desugar on the JVM? What does a top-level `val String.lastIndex get() = length - 1` compile to?

level: seniorimportance: should knowfreq 35%

basics

~20 s

On the JVM it becomes a plain static method (a getter, and a setter for var) that takes the receiver as its first argument. There is no field; calling the property just calls that static method.

open as a page

When should you reach for an extension property versus an extension function or a real member property? What are the design trade-offs?

level: principalimportance: should knowfreq 25%

basics

~20 s

Use an extension property for a cheap, no-argument, value-like accessor that reads naturally as a noun. Use an extension function when it takes arguments or does real work. Use a real member when you own the class and need stored state.

open as a page