Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is the JavaFX SimpleDoubleProperty.addListener method wrongly declared?

Tags:

java

javafx

In my code, I am interested about listening to changes to a SimpleDoubleProperty.

Here's the signature for it's addListener method:

public void addListener(ChangeListener<? super Number> listener)

How does a property that fires changes to a Double value, require it's listener to listen to Number or one of it's super types?. Shouldn't it be defined like that:

 public void addListener(ChangeListener<? extends Double> listener)

An example class that has the property:

HasObservableProperty.java

public class HasObservableProperty {
     private final SimpleDoubleProperty progress = new SimpleDoubleProperty();
     ...
     public void addProgressChangeListener(ChangeListener<? super Number> listener) {
         progress.addListener(listener);
     }
}

And so it goes, I have a listener defined as:

class ProgressBarNotifier implements ChangeListener<Number> {
    private final ProgressBar progress_bar;

    public ProgressBarNotifier(ProgressBar bar) {
        progress_bar = bar;
   }
   @Override
   public void changed(ObservableValue<? extends Number> progress, Number oldValue,
                        Number newValue) {
        progress_bar.setProgress((Double)newValue);
   }
}

See how I was forced to cast the changed value to Double:

progress_bar.setProgress((Double)newValue);

This shouldn't be the case if the addListener method had the suggested signature.

So, the question is, is SimpleDoubleProperty.addListener method wrongly declared?

PS: what applies to SimpleDoubleProperty seems to apply to other observables, like SimpleStringProperty.

like image 615
MadeOfAir Avatar asked Nov 02 '22 03:11

MadeOfAir


1 Answers

The simple answer is "because that's how it was done". Note that, as Uluk points out in the comments, you're not forced to cast: you can call doubleValue() on the Number reference you receive in the listener method.

There's a pretty complex hierarchy behind DoubleProperty. The relevant part is that DoubleProperty implements ObservableDoubleValue extends ObservableNumberValue extends ObservableValue<Number>. ObservableValue<T> defines an addListener(ChangeListener<? super T>) method, so DoubleProperty has to define addListener(ChangeListener<? super Number>).

Was this the best way to do it? I actually don't think so: I think it would have been possible to make ObservableNumberValue generic: ObservableNumberValue<T extends Number> extends ObservableValue<T> and then ObservableDoubleValue extends ObservableNumberValue<Double>, at which point the addListener(...) method would be relaxed to addListener(ChangeListener<? super Double>), which is what you're looking for.

There are whole other parts of the hierarchy: Property, WritableDoubleValue, WritableNumberValue, WritableValue, etc, that may prevent this structure; but at any rate such a change now would be impossible, so we are stuck with what we have whether it's a mistake or whether there's some complex reason it has to be this way that I'm not aware of. It's occasionally annoying, but as pointed out you can always use doubleValue().

In many cases like the one you show, you can also use a binding, e.g. progressBar.progressProperty().bind(progress);

like image 183
James_D Avatar answered Nov 15 '22 01:11

James_D