Skip to content
This repository has been archived by the owner on Sep 28, 2024. It is now read-only.

Properties

Edvin Syse edited this page Feb 27, 2016 · 15 revisions

WikiDocumentationProperties

JavaFX Properties enhancements

TornadoFX features a small set of delegates for Kotlin properties.

Property delegate

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.

Single Assign Property

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:

  1. Since it is a mutable var, the myButton property could mistakenly be assigned multiple times
  2. 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

Clone this wiki locally