Boilerplate

This interface is NOT USED, it is only here for Documentation.

ObservableValue and Property both have a generic type parameter, which determines the type for the property's value. However, JVM's generics are 2nd class citizens due to type erasure.

When we compile :

val a : Property = ...

the generic type Int is erased, and the actual type is Property<*>. Most of the time, this isn't a problem, but for Property and ObservableValue it is.

I need to define functions, which operate on string properties. For example, I want to add two Property. We could define :

fun add( a : Property, b : Property ) = ...

But this won't work, because this function has exactly the same signature as :

fun add( a : Property, b : Property ) = ...

(remember the generic type is erased).

My solution is to create autogenerated boilerplate such as :

interface ObservableInt : Observable
interface ObservableFloat : Observable

interface IntProperty : Property, ObservableInt
interface FloatProperty : Property, ObservableFloat

class SimpleIntProperty(initialValue : Int) : SimpleProperty(initialValue), IntProperty
class SimpleFloatProperty(initialValue : Float) : SimpleProperty(initialValue), FloatProperty
...

There is quite a lot of boilerplate, because there are more than 2 types other than Int and Float, and more classes/interfaces other than ObservableValue, Property and SimpleProperty. We also need to duplicate each piece of boilerplate for nullable values (which glok calls Optional). So we also need :

interface ObservableOptionalInt : Observable
interface OptionalIntProperty : Property, ObservableOptionalInt
class SimpleOptionalIntProperty(initialValue : Int?) : SimpleProperty(initialValue), OptionalIntProperty
...

But at least we can now define addition for ints and floats :

fun add( a : ObservableInt, b : ObservableInt ) : ObservableInt = ...
fun add( a : ObservableFloat, b : ObservableFloat ) : ObservableFloat = ...

Note, we can only use these functions if we have an IntProperty or FloatProperty. If we have Property<Int> or Property<Float> we cannot use them.

However, there are more problems...

Consider SpinnerBase, the base class for IntSpinner and FloatSpinner etc. It is ugly, because the valueProperty cannot be defined as :

val valueProperty : Property

without losing the advantages gained by the boilerplate. So we need two generic type parameters. V for the type held (such as Int or Float), and P, which is the type of the property (such as IntProperty or FloatProperty). It's ugly, but works.

Now consider SingleSelectionModel. It has a selectedItemProperty, and this time we can't use the same hack, without getting into a real mess. So its type is ObservableValue<T?>. and our boilerplate isn't being used :-( selectedItemProperty is a 2nd class citizen. Grr.

It's all a bit of a mess :-(