Write-only properties with compile-time errors can be achieved since Kotlin 1.0, using a workaround based on @Deprecated
.
Implementation
Kotlin allows to mark functions deprecated with level ERROR
, which leads to a compile-time error when called. Annotating the get
accessor of a property as error-deprecated, combined with a backing field (so that private reads are still possible), achieves the desired behavior:
class WriteOnly {
private var backing: Int = 0
var property: Int
@Deprecated("Property can only be written.", level = DeprecationLevel.ERROR)
get() = throw NotImplementedError()
set(value) { backing = value }
val exposed get() = backing // public API
}
Usage:
val wo = WriteOnly()
wo.property = 20 // write: OK
val i: Int = wo.property // read: compile error
val j: Int = wo.exposed // read value through other property
The compile error is quite helpful, too:
Using 'getter for property: Int' is an error. Property can only be written.
Use cases
The main use case are obviously APIs that allow properties to be written, but not read:
user.password = "secret"
val pw = user.password // forbidden
Another scenario is a property which modifies the internal state, but is not stored itself as a field. (Could be done more elegantly using different design).
body.thrust_force = velocity
body.gravity_force = Vector(0, 0, 9.8)
// only total force accessible, component vectors are lost
val f = body.forces
This pattern is also useful for DSLs of the following kind:
server {
port = 80
host = "www.example.com"
}
In such cases, values are simply used as one-time settings, and the write-only mechanism described here can prevent accidentally reading a property (which might not be initialized yet).
Limitations
Since this feature was not designed for this use case, it comes with certain limitations:
If accessed using a property reference, the compile-time error turns into a runtime error:
val ref = wo::property
val x = ref.get() // throws NotImplementedError
The same is true for reflection.
This functionality cannot be outsourced into a delegate, because an error-deprecated getValue()
method cannot be used with by
.