Delegation Quiz
KOTLIN › Types & Classes
In class Derived(b: Base) : Base by b, who implements the members of Base?
- Derived must manually override every member itself, one by one
- The compiler emits forwarding methods that delegate to b
- The members stay abstract until they are first invoked
- Reflection resolves them at runtime and invokes them then
Answer: The compiler emits forwarding methods that delegate to b
The by keyword makes the compiler generate methods that forward each interface member to the delegate object b, so Derived needs no manual implementations.
A delegate object b has print() that reads its own message. Derived overrides message but uses Base by b. What does derived.print() show?
- The overridden message declared in Derived
- A compile error caused by member ambiguity
- b's own message, ignoring Derived's override
- null, since the override hid b's backing field
Answer: b's own message, ignoring Derived's override
A delegate cannot see members overridden in the deriving class; b.print() uses b's own message, so Derived's override is ignored by the delegate's method.
Which operator function signature is required for a read-only (val) custom delegate?
- operator fun getValue(thisRef: Any?, property: KProperty<*>): T
- operator fun get(index: Int): T for reading from a collection-like delegate
- operator fun getValue(value: T): T for returning a value from the delegate
- fun getValue(property: String): T for a custom delegate's property lookup
Answer: operator fun getValue(thisRef: Any?, property: KProperty<*>): T
A val delegate needs operator fun getValue(thisRef, property: KProperty<*>) returning the property type; the property parameter is a KProperty reflection object.
Which standard delegate's handler runs BEFORE assignment and can reject the new value?
- Delegates.observable
- lazy
- Delegates.notNull
- Delegates.vetoable
Answer: Delegates.vetoable
vetoable invokes its handler before applying the change and returning false vetoes it; observable fires after assignment and cannot reject it.
What is the default thread-safety mode of by lazy { ... }?
- LazyThreadSafetyMode.NONE (explicitly opt out)
- LazyThreadSafetyMode.SYNCHRONIZED (the default mode)
- LazyThreadSafetyMode.PUBLICATION (requires opting in)
- It is not thread-safe unless you add synchronization
Answer: LazyThreadSafetyMode.SYNCHRONIZED (the default mode)
Without an argument, lazy uses SYNCHRONIZED, locking so only one thread initializes the value; PUBLICATION and NONE must be requested explicitly.
For class User(val map: MutableMap<String, Any?>) { var age: Int by map }, what happens on user.age = 30?
- A compile error, since maps are read-only delegates here
- It throws, because an Int can't be stored in Any?
- map["age"] is set to 30 via the map's setValue
- Nothing happens; map-backed delegates ignore all writes
Answer: map["age"] is set to 30 via the map's setValue
MutableMap provides a setValue delegate, so assigning to a map-backed var writes the value under the property name key (map["age"] = 30).
When delegating a property with by, what does the compiler generate behind the scenes?
- A hidden prop$delegate field the accessors route through
- An anonymous subclass generated from the property type
- A reflective proxy object invoked on every single access
- Nothing; the property is inlined as a compile-time constant
Answer: A hidden prop$delegate field the accessors route through
The compiler stores the delegate in a hidden prop$delegate field and routes the property's get/set through the delegate's getValue/setValue, passing this and the KProperty reference.
What is the role of an operator fun provideDelegate(thisRef, property) on a delegate?
- It is called on every property read to recompute the delegate each time
- It replaces getValue entirely and is required for every delegate type
- It runs once when the delegate is bound, before any getValue call
- It is only useful for map-backed delegates and nothing else
Answer: It runs once when the delegate is bound, before any getValue call
provideDelegate is invoked once at delegate-creation (binding) time, so you can run validation or return a different delegate instance before any getValue/setValue access happens.
To write a custom var delegate without hand-writing operator functions, which standard library interface should you implement?
- ReadWriteProperty<T, V>, which also extends ReadOnlyProperty
- ReadOnlyProperty<T, V> alone, which only handles val delegates
- Lazy<V>, which caches a value but does not support setValue
- KProperty<V>, which describes a property but is not a delegate
Answer: ReadWriteProperty<T, V>, which also extends ReadOnlyProperty
ReadWriteProperty supplies both getValue and setValue (and extends ReadOnlyProperty), so implementing it gives you a working var delegate; ReadOnlyProperty alone only supports val.
When does the handler passed to Delegates.observable run, and what can it do?
- Before assignment, where returning false vetoes the change
- Only the first time the property is read, not on writes
- Just once, at the moment the delegate is first created
- After assignment, for notification only; can't reject it
Answer: After assignment, for notification only; can't reject it
observable fires its callback with old and new values after the assignment is applied; unlike vetoable it has no return value and cannot block the change.
What happens with var counter: Int by lazy { 0 }?
- The initializer lambda runs again on every assignment to the property
- Compile error: lazy returns Lazy<T>, which only has getValue
- It compiles, but any later writes are ignored and the value stays put
- It compiles, but the first assignment throws an exception at runtime
Answer: Compile error: lazy returns Lazy<T>, which only has getValue
Lazy<T> exposes only a getValue operator, so it cannot back a var; the compiler rejects using lazy with a mutable property.
Under concurrent first access, how does LazyThreadSafetyMode.PUBLICATION behave?
- A lock lets only one thread run the initializer while the rest wait
- The initializer never runs until you explicitly force the lazy value
- Several threads may run it, but the first completed value is cached
- Each thread keeps its own separate cached value, so nothing is shared
Answer: Several threads may run it, but the first completed value is cached
PUBLICATION allows several threads to race the initializer without a lock; the first value to be set atomically wins and all callers then see that single cached value.
Inside a custom getValue implementation, how do you read the delegated property's declared name?
- property.name; the KProperty parameter exposes it
- thisRef.toString(); the receiver object is not the property name
- There is no way; the declared name is erased before getValue runs
- property.getValue(); that calls the delegate again instead of reading a name
Answer: property.name; the KProperty parameter exposes it
The KProperty<*> argument passed to getValue/setValue exposes the property's name (and other metadata), which is exactly how map-backed delegates look up the key.
What problem does Delegates.notNull() solve?
- It makes a val initialize lazily on its very first read
- It automatically converts a nullable type into a non-null one for you
- It observes a property and logs each change made to it
- A non-null var with deferred init that throws if read too early
Answer: A non-null var with deferred init that throws if read too early
notNull provides a non-null var whose initialization is deferred; reading it before a value is set throws, which is useful when the value can't be supplied at construction time.