-
Notifications
You must be signed in to change notification settings - Fork 271
Properties
Wiki ▸ Documentation ▸ Properties
TornadoFX features a small set of delegates for Kotlin properties.
Generating JavaFX properties is a bit verbose, even in Kotlin. The traditional way looks like this:
private val fooProperty by lazy { SimpleObjectProperty<T>() }
fun fooProperty() = fooProperty
var foo: T
get() = fooProperty.get()
set(value) = fooProperty.set(value)
A private property called
foo
with a method to expose the property and getters and setters for the value
TornadoFX improves upon this structure in several ways to reduce boiler plate. Firstly, you can use a property delegate to create the getter and setter:
private val fooProperty = SimpleObjectProperty<String>()
fun fooProperty() = fooProperty
var foo = property(fooProperty)
A private property with a method to expose the property and automatic generation of getters and setters
By using the property delegate, the code is smaller and easier to read. However, there is an even shorter version available:
var foo by property<String>()
fun fooProperty() = getProperty(ClassName::foo)
A delegate that wraps the generated property to create getters and setters and a method that extracts the property from the delegate.
This last version requires generics, so it might not be suited for mobile applications due to performance concerns. ClassName
above is a reference to the class where there property is defined.
It is not uncommon to want to save Nodes to properties in a given View
declaration, especially when you are using Type Safe Builders. This way you have access to the Nodes later to manipulate them, such as turning them into RxJava Observables or ReactFX EventStreams.
The problem is you typically declare the Node
structure in the init { }
block, which exists outside the constructor. You cannot save the Node
to a val
property. You have to do it later with a var
. If you want the property to not be nullable, you will have to use the lateinit
modifier.
class MyView: View() {
override val root = VBox()
lateinit var myButton: Button
init {
with(root) {
myButton = button("New Entry")
}
}
//Using RxJavaFX to turn button events into Observable
fun getEvents(): Observable<ActionEvent> =
myButton.toNodeEvents(ActionEvent.Action)
}
There are still two problems:
- Since it is a mutable
var
, themyButton
property could mistakenly be assigned multiple times - Access to the
myButton
property is not necessarily threadsafe
You can remedy these two problems by using the singleAssign()
property delegate.
class MyView: View() {
override val root = VBox()
var myButton: Button by singleAssign()
init {
with(root) {
myButton = button("New Entry")
}
}
//Using RxJavaFX to turn button events into Observable
fun getEvents(): Observable<ActionEvent> =
myButton.toNodeEvents(ActionEvent.Action)
}
Now myButton
can only be assigned a value once. Any subsequent assignment attempts will result in an exception. If the property is accessed before it is assigned, that will also throw an exception.
It is also threadsafe with the highest concurrency achievable. If you are concerned about synchronization overhead, you can explicitly pass an argument to specify no synchronization.
var myButton: Button by singleAssign(SingleAssignThreadSafetyMode.NONE)
If your application is at all multithreaded, it is recommended to not disable the synchronization.
Next: Type Safe Builders