skip to content

How does Delegates.vetoable differ from observable, and how do you use its return value to reject an assignment?

level: middleimportance: must knowfreq 50%

answer

  1. vetoable returns Boolean: true=accept, false=reject
  2. Runs BEFORE the write
  3. Veto is silent — no exception
  4. Throw inside lambda to fail loudly
  5. Both built on ObservableProperty (before/afterChange)

basics

~10 s

Delegates.vetoable runs a check before storing a new value. Your lambda returns true to accept the change or false to reject it. If you return false, the property keeps its old value.

solid answer

~40 s

Delegates.vetoable(initialValue) { property, oldValue, newValue -> Boolean } is like observable but its callback returns a `Boolean` and runs *before* the field is updated. Return `true` to allow the assignment, `false` to veto it — on veto the backing field keeps the old value and the write is silently discarded (no exception, no error). This makes it ideal for validation/guards, e.g. rejecting negative numbers or empty strings. The lambda still receives KProperty, old, and new. Because the decision is made before writing, you read `newValue` (the proposed value) to validate, not the stored field. It is defined in `kotlin.properties.Delegates` and returns a `ReadWriteProperty`.

code

kotlin · 11 lines
kotlin
import kotlin.properties.Delegates

var positive: Int by Delegates.vetoable(1) { _, old, new ->
    if (new > 0) true else { println("rejected $new, keeping $old"); false }
}

fun main() {
    positive = 5    // ok -> 5
    positive = -3   // rejected -3, keeping 5
    println(positive) // 5
}

go deeper

for a junior

Knows vetoable can reject by returning false and runs before the write.

for a middle

Correctly contrasts return type and timing with observable and knows the veto is silent.

for a senior

Explains throwing to veto loudly, the ObservableProperty before/afterChange design, and the silent-failure pitfall for callers.

for a principal

Weighs vetoable against explicit setters/validation layers, notes the lack of caller feedback as an API smell, and considers testability and error-reporting strategy.

## observable vs vetoable Both come from `kotlin.properties.Delegates` and both take an initial value plus a lambda with `(property, oldValue, newValue)`. The difference is **timing and return type**: | | `observable` | `vetoable` | |---|---|---| | Lambda return | `Unit` | `Boolean` | | Runs | **after** the write | **before** the write | | Can reject? | No | **Yes** (return `false`) | ## Signature ```kotlin fun <T> vetoable( initialValue: T, onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean ): ReadWriteProperty<Any?, T> ``` ## The veto rule The lambda is a **predicate** evaluated against the proposed `newValue`: - Return `true` → the assignment proceeds and the field is updated. - Return `false` → the assignment is **rejected silently**. The field keeps its old value. **No exception is thrown.** That silent behaviour is important: callers do not automatically learn the write failed. If you need to signal rejection loudly, throw inside the lambda (which aborts the assignment with an exception) or expose a separate result. ## Example: reject invalid values ```kotlin import kotlin.properties.Delegates class Volume { var level: Int by Delegates.vetoable(0) { _, _, new -> new in 0..100 // accept only 0..100 } } fun main() { val v = Volume() v.level = 30 // accepted -> 30 v.level = 250 // vetoed -> stays 30 (no error) println(v.level) // 30 } ``` ## Throwing to veto loudly ```kotlin var age: Int by Delegates.vetoable(0) { _, _, new -> require(new >= 0) { "age must be >= 0" } // throws on bad input true } ``` Here a bad value throws `IllegalArgumentException` instead of being silently ignored. ## Internals Both `observable` and `vetoable` are built on the abstract `ObservableProperty<T>` base class. `observable` overrides `afterChange` (returns nothing), `vetoable` overrides `beforeChange` (returns the Boolean gate). You can subclass `ObservableProperty` directly for more control.

  • If vetoable returns false, what happens to the property and the caller?
    The field keeps its old value and the write is silently discarded — no exception reaches the caller.
  • How can you make a rejected assignment raise an error?
    Throw inside the lambda (e.g. require/check); that aborts the assignment with an exception instead of a silent veto.
  • Which base class powers both observable and vetoable?
    ObservableProperty<T> — observable overrides afterChange, vetoable overrides beforeChange.

vetoable is a bouncer checking IDs at the door — returns 'yes you may enter' or 'no'; observable is the guest book signed only after you're already inside.

saying these in an interview costs you the question

  • Saying a vetoed assignment throws an exception by default
  • Returning the value instead of a Boolean from the lambda
  • Validating oldValue instead of the proposed newValue
  • Thinking vetoable runs after the write like observable
  • Believing the caller gets a return value indicating success

context