0

I am using setCellValueFactory to configure my TableView column using a lambda expression. The underlying property is a SimpleLongProperty. I have found that the lambda expression

ageColumn.setCellValueFactory(cellData -> cellData.getValue().ageProperty());

does not work if ageProperty is a SimpleLongProperty.

I have found the solution to this problem is to add the asObject method as follows:

ageColumn.setCellValueFactory(cellData -> cellData.getValue().ageProperty().asObject());

It is not clear to me why adding the asObject method causes the lambda to return the right type of an ObservableValue. I understand it has something to do with the fact that SimpleLongProperty implements ObservableValue(Number)rather than ObservableValue(Long) but I can't see why this matters. In any case, how does asObject() fix this problem?

ClarkS
  • 100
  • 1
  • 9
  • How is ageColumn declared? – James_D Dec 14 '14 at 14:15
  • It is declared as @FXML private TableColumn ageCol; I know I can fix the problem by declaring it as but I don't want to do that. I am trying to understand what asObject() does. – ClarkS Dec 14 '14 at 18:10

1 Answers1

3

This is basically just a type matching issue.

TableColumn<S, T> defines a method setCellValueFactory(Callback<CellDataFeatures<S,T>, ObservableValue<T>> factory).

Since you have defined the column as a TableColumn<Configuration, Long>, its setCellValueFactory is expecting a Callback<CellDataFeatures<Configuration, Long>, ObservableValue<Long>> as the parameter; i.e. it is expecting something that defines a method

public ObservableValue<Long> call(CellDataFeatures<Configuration, Long> cellDataFeatures) ;

If you pass this as a lambda expression of the form

cellData -> <some expression>

then the compiler will infer that cellData is a CellDataFeatures<Configuration, Long> and will require that the expression evaluates to an ObservableValue<Long>.

So cellData.getValue() will evaluate to a Configuration, and if you define the age property in Configuration as a LongProperty, then of course cellData.getValue().ageProperty() evaluates to a LongProperty. This causes a type mismatch and the compiler complains. Remember that the compiler is expecting this expression to evaluate to an ObservableValue<Long>; as you observed LongProperty implements ObservableValue<Number>, not ObservableValue<Long>. Thus your expression evaluates to an ObservableValue<Number> instead of the required type of ObservableValue<Long>.

Both the fixes you mentioned work: changing the column to a TableColumn<Configuration, Number> works because the compiler is then expecting a lambda expression evaluating to an ObservableValue<Number> (which it is getting).

Calling asObject() on the LongProperty works too. As the Javadocs state, this method creates an ObjectProperty<Long> that is bidirectionally bound to the LongProperty. Since ObjectProperty<T> implements ObservableValue<T>, cellData.getValue().ageProperty().asObject() is an ObservableValue<Long> as required.

James_D
  • 201,275
  • 16
  • 291
  • 322
  • What if i use generic type or boolean ? As i'm using a SimpleObject> it does work ! – Hassam Abdelillah Apr 27 '16 at 19:13
  • This problem only arises with the primitive value wrappers for numeric types: i.e. `IntegerProperty`, `LongProperty`, `DoubleProperty`. These all implement `Property` not `Property`, etc. The `asObject()` method is just there to fix this particular type issue, it, for example, converts `IntegerProperty` to `Property`. This doesn't arise in any other scenario: `BooleanProperty` implements `ObjectProperty` already, and of course the generically typed `ObjectProperty` is already the type you need. – James_D Apr 27 '16 at 19:26
  • I think i have to open a new thread because it don't sove it that way ! – Hassam Abdelillah Apr 27 '16 at 19:41