How does Delegates.vetoable differ from observable, and how do you use its return value to reject an assignment?
answer
- vetoable returns Boolean: true=accept, false=reject
- Runs BEFORE the write
- Veto is silent — no exception
- Throw inside lambda to fail loudly
- Both built on ObservableProperty (before/afterChange)
basics
~10 sDelegates.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 sDelegates.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 linesimport 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
Knows vetoable can reject by returning false and runs before the write.
Correctly contrasts return type and timing with observable and knows the veto is silent.
Explains throwing to veto loudly, the ObservableProperty before/afterChange design, and the silent-failure pitfall for callers.
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