0

(Following this question, this is what prompted it)

I have a model class with a LongProperty:

public class Model { 
    private final SimpleLongProperty number = new SimpleLongProperty(this, "number"); 
    public long getNumber() { return number.get(); }
    public void setNumber(long number) { this.number.set(number); }
    public LongProperty numberProperty() { return number; }
}

Now, in my controller I have a TableColumn<Model, Long> colNumber which I want to bind to this property. I know I can use PropertyValueFactory, but I don't like the idea of giving the property by name when I can pass it programmatically, and have the compiler/ide spell-check me. Basically I want to do something like this (I actually want to make it more concise, example in the end):

colNumber.setCellValueFactory( cdf -> cdf.getValue().numberProperty() );

but this gives me a compilation error:

java: incompatible types: bad return type in lambda expression javafx.beans.property.ObjectProperty cannot be converted to javafx.beans.value.ObservableValue

As I said, I know I can use PropertyValueFactory, and also have static final strings for the property names, but I find it less elegant. Is there a way to make this programmatic approach work? Some casting-magic?

Appendix:
The actual way I wanted to make it is with a helper method:

private <S,T> Callback<CellDataFeatures<S,T>, ObservableValue<T>> propertyFactory(Callback<S, ObservableValue<T>> callback) {
    return cdf-> callback.call(cdf.getValue());
}

and then I can just use

colNumber.setCellValueFactory(propertyFactory(Model::numberProperty));

which keeps my code very concise and readable, and has the compiler checking me for typos etc.

Community
  • 1
  • 1
Itai
  • 6,641
  • 6
  • 27
  • 51
  • Err... I can't get things right today. The examples above are flawed because my code wasn't what I thought it was. I'll look into it and fix it. Sorry about that. – Itai Jan 05 '16 at 21:29

1 Answers1

1

You can do

colNumber.setCellValueFactory( cdf -> cdf.getValue().numberProperty().asObject() );

I think (I'd need to test, but this seems right) you could also take advantage of autoboxing and unboxing in your model, and implement the property as an ObjectProperty<Long>:

public class Model { 
    private final ObjectProperty<Long> number = new SimpleObjectProperty<>(this, "number", 0L); 
    public long getNumber() { return number.get(); }
    public void setNumber(long number) { this.number.set(number); }
    public ObjectProperty<Long> numberProperty() { return number; }
}

One disadvantage to this approach is it would not allow any of the arithmetic bindings, e.g. you couldn't do someValue.bind(model.numberProperty().multiply(2)); etc. (Another is that you could call model.numberProperty().set(null); by accident, and wreak all kinds of havoc.)

One other solution, of course, is to make your table column a TableColumn<Model, Number>, but there may be other reasons not to do that.

FWIW, I would definitely advocate avoiding PropertyValueFactory. That was a convenience class introduced in JavaFX 2.0 (i.e. before we had lambdas), when it was very verbose to implement the callback that allowed for compiler checking. Now it is basically redundant (and should be deprecated, imho, or should at least implement a utility method of the kind you outline).

James_D
  • 201,275
  • 16
  • 291
  • 322
  • Thank you. I'm glad to hear `PropertyValueFactory` is considered not optimal solution, as I myself don't like it for the same reasons. – Itai Jan 05 '16 at 21:45