Search code examples
genericsjavafxjavabeans

How to eliminate raw types for ChangeListener


I’m learning binding in JavaFX. And I’m confused with generic types. Here I have an example:

public static void main(String[] args) {

   Bill electricBill = new Bill();

   electricBill.amountDueProperty().addListener(new ChangeListener(){
      @Override 
      public void changed(ObservableValue o,Object oldVal, Object newVal)
      {
         System.out.println("Electric bill has changed!");
      }
   });

   electricBill.setAmountDue(100.00);

}

It works perfectly but I decided to check what will be this line: electricBill.amountDueProperty().addListener(new ChangeListener(){…});

without raw type. I found that the only variant that works is

electricBill.amountDueProperty().addListener(new ChangeListener<Object>(){

    @Override 
    public void changed(ObservableValue<? extends Object> o, Object oldVal, Object newVal) {…}
});

As I understand all other variants raises compiler error due to wildcard bounds - one extends T, another - needs superclass:

void addListener(ChangeListener<? super T> listener) ChangeListener::changed(ObservableValue<? extends T> observable, T oldValue, T newValue)

Is there any way to make parameters of changed() more specific?


Solution

  • Since amountDueProperty() returns a DoubleProperty, which implements ObservableValue<Number>, you can register a ChangeListener<T> for any T super Number (i.e T can be Number or any superclass of Number):

    electricBill.amountDueProperty().addListener(new ChangeListener<Number>() {
        @Override
        public void changed(ObservableValue<? extends Number> obs, Number oldValue, Number newValue) {
            double newBill = newValue.doubleValue();
            // ...
        }
    });
    

    or, of course, using lambda expressions:

    electricBill.amountDueProperty().addListener((obs, oldValue, newValue) -> {
        // newValue is a Number, by type inference:
        double newBill = newValue.doubleValue();
        // ...
    });