JavaFX object traits

Michael Strauß michaelstrau2 at gmail.com
Sat Sep 2 22:09:13 UTC 2023


There's a proposal to add a common interface that identifies JavaFX
objects that can hold an Observable<Object, Object> property map:
https://github.com/openjdk/jfx/pull/1215

The reason for this is obvious: allow JavaFX objects that can hold
properties to be consumed by code without depending on brittle
`instanceof` checks. The problem is real, but I think we can do much
better than that.

Why have a common interface at all? Well, of course it allows
consumers to treat different objects in a uniform way. But there's
more to it: the interface specifies the _meaning_ of the method; it
guarantees that `Foo::getProperties` and `Bar::getProperties` are, in
fact, not only methods with the same name, but the same semantics.

`getProperties` and `hasProperties` is one example of such commonality
between dissimilar classes like `Node` and `MenuItem`. But I've come
across other examples in my own projects. For example, I'd like to
consume JavaFX objects that have the `visible` and `disable`
properties. Other applications will have different use cases, but
since we don't know all possible use cases, it's hard to come up with
a set of useful combinations of properties and methods.

However, I think we can use the Java type system to allow applications
to compose types that fit their unique use case.

We begin by identifying common properties, and create trait interfaces
that describe those properties:

    public final class javafx.scene.Trait {
        public interface Visible {
            BooleanProperty visibleProperty()
            default boolean isVisible()...
            default void setVisible(boolean value)...
        }
        public interface Disable {
            BooleanProperty disableProperty()
            default boolean isDisable()...
            default void setDisable(boolean value)...
        }
        public interface Properties {
            ObservableMap<Object, Object> getProperties()
            default boolean hasProperties()...
        }
        ...
    }

These interfaces can now be implemented by all relevant JavaFX
classes. This includes `Node`, `MenuItem`, and `Tab`, but applications
are free to implement these trait interfaces themselves.

Applications can now consume objects that implement any combination of
traits, which gives applications the much-needed flexibility to use
shared code for all kinds of JavaFX objects:

<T extends Trait.Properties & Trait.Visible>
void doSomething(T node) {
    node.getProperties().put("foo", "bar");
    node.setVisible(true);
}

<T extends Trait.Text & Trait.Graphic>
void doAnotherThing(T node) {
    node.setText("hello");
    node.setGraphic(myGraphic);
}

What do you think?


More information about the openjfx-dev mailing list