# Extension Properties

Just as functions can be extension functions, properties can be extension properties.

The receiver type specification for extension properties is similar to the syntax for extension functions—the extended type comes right before the function or property name:

fun ReceiverType.extensionFunction() { ... }
val ReceiverType.extensionProperty: PropType
  get() { ... }

An extension property requires a custom getter. The property value is computed for each access:

// ExtensionProperties/StringIndices.kt
package extensionproperties
import atomictest.eq

val String.indices: IntRange
  get() = 0 until length

fun main() {
  "abc".indices eq 0..2
}

Although you can convert any extension function without parameters into a property, we recommend thinking about it first. The reasons described in [Property Accessors](javascript:void(0)) for choosing between properties and functions also apply to extension properties. Preferring a property over a function makes sense only if it’s simple enough and improves readability.

You can define a generic extension property. Here, we convert firstOrNull() from [Introduction to Generics](javascript:void(0)) to an extension property:

// ExtensionProperties/GenericListExt.kt
package extensionproperties
import atomictest.eq

val <T> List<T>.firstOrNull: T?
  get() = if (isEmpty()) null else this[0]

fun main() {
  listOf(1, 2, 3).firstOrNull eq 1
  listOf<String>().firstOrNull eq null
}

The Kotlin Style Guide (opens new window) recommends a function over a property if the function throws an exception.

When the generic argument type isn’t used, you may replace it with *. This is called a star projection:

// ExtensionProperties/ListOfStar.kt
package extensionproperties
import atomictest.eq

val List<*>.indices: IntRange
  get() = 0 until size

fun main() {
  listOf(1).indices eq 0..0
  listOf('a', 'b', 'c', 'd').indices eq 0..3
  emptyList<Int>().indices eq IntRange.EMPTY
}

When you use List<*>, you lose all specific information about the type contained in the List. For example, an element of a List<*> can only be assigned to Any?:

// ExtensionProperties/AnyFromListOfStar.kt
import atomictest.eq

fun main() {
  val list: List<*> = listOf(1, 2)
  val any: Any? = list[0]
  any eq 1
}

We have no information whether a value stored in a List<*> is nullable or not, which is why it can be only assigned to a nullable Any? type.

Exercises and solutions can be found at www.AtomicKotlin.com.