Custom Property Delegates

getValue and setValue

Any class with operator fun getValue (and setValue for var) can be a delegate. The standard interface ReadWriteProperty<Owner, T> gives the right signatures.

Example

A delegate that trims and capitalises names, and one that remembers a history of values.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

class Trimmed : ReadWriteProperty<Any?, String> {
    private var value = ""
    override fun getValue(thisRef: Any?, property: KProperty<*>) = value
    override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        this.value = value.trim().split(" ").filter { it.isNotEmpty() }
            .joinToString(" ") { it.lowercase().replaceFirstChar(Char::uppercase) }
    }
}

class History<T>(initial: T) {
    val values = mutableListOf(initial)
    operator fun getValue(thisRef: Any?, property: KProperty<*>): T = values.last()
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { values += value }
}

class Student {
    var fullName by Trimmed()
    val gradeHistory = History("C")
    var grade by gradeHistory
}

fun main() {
    val s = Student()
    s.fullName = "   rAHUL   kumar  "
    println("'${s.fullName}'")
    s.grade = "B"
    s.grade = "A"
    println("Grade: ${s.grade}, history: ${s.gradeHistory.values}")
}

Output

'Rahul Kumar'
Grade: A, history: [C, B, A]

Where You See Delegates in Android

class ProfileFragment : Fragment() {
    private val viewModel: ProfileViewModel by viewModels()          // lazy ViewModel
    private val args: ProfileFragmentArgs by navArgs()               // Navigation safe args
}

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }                      // State<T> delegate
    Button(onClick = { count++ }) { Text("Clicked $count times") }
}

val Context.dataStore by preferencesDataStore(name = "settings")    // DataStore