I am trying to follow this JavaFX 2 guide:
http://docs.oracle.com/javafx/2/ui_controls/table-view.htm#CJAGAAEE
I use Scala instead of Java and it looks like this for me:
<TableView fx:id="test">
<columns>
<TableColumn prefWidth="75.0" text="Message" />
</columns>
</TableView>
And code:
val c = test.getColumns.get(0) // Get the first column.
c.setCellValueFactory(new PropertyValueFactory[Foo, _]("message")) // Foo is my model with a single SimpleStringProperty called "message".
val observableFoos = FXCollections.observableList(foos)
test.setItems(observableFoos)
The problem I have is that the setCellValueFactory line causes:
error: class type required but
javafx.scene.control.cell.PropertyValueFactory[com.myapp.models.Foo,
_] found c.setCellValueFactory(new PropertyValueFactoryFoo, _)
I don’t understand how I am supposed to use this method. If I replace _ with String, then I get:
error: type mismatch; found :
javafx.scene.control.cell.PropertyValueFactory[com.myapp.models.Foo,String]
required:
javafx.util.Callback[javafx.scene.control.TableColumn.CellDataFeatures[com.myapp.models.Foo,?0],javafx.beans.value.ObservableValue[?0]]
where type ?0 c.setCellValueFactory(new PropertyValueFactoryFoo,
String)
I can confirm that everything works fine if I remove the setCellValueFactory line — I just don’t see any content in the table — just blank rows as expected..
TL;DR: It is easy in java to silently bypass type safety with respect to type parameters.
Scala won’t let you do that, short of performing an explicit cast.
In addition, using
Table.getColumnsto retrieve the column means that the we lose the type of cell.Solutions: cast your
TableColumninstance to the proper type, or instantiate the column by hand.The first problem lies in the signature of
Table.getColumns: it returns aObservableList[TableColumn[S,_]](see http://docs.oracle.com/javafx/2/api/javafx/scene/control/TableView.html#getColumns()).So your
cval is typed asTableColumn[Foo,_].This means that you lose the type of the cell content (denoted by the second type parameter).
The only solution here is to properly type the column (
c) using a cast:This is quite logical: you have a list of columns where each column can contain objects of a different type. We would face the same dilemma with a scala List containing objects of different types, all unknown a priori: only a cast (or a match on the type, which is a cast in disguise) will let you get back the a specific type at compile time.
Note also that in many JavaFx examples around the web, people don’t retrieve the columns through
Table.getColumns. Instead, they instantiate them by hand and then callsetCellValueFactoryon it right after.Doing the same would solve your problem (without any need for a cast), as you will yourself specify the type parameters:
Now, you might look at some java examples and notice that they don’t actually provide any type parameter when instantiating their
TableColumn:And indeed it does compile. How so? Well the sad truth is that the above code is not safer than doing a cast, as it relies on java’s backward compatibility for pre-generics aware classes.
In other words, because
cis typed as justTableColumnand notTableColumn<?, String>by example, the java compiler treat it as a non generic class and not perform any type checking regarding the type parameters ofTableColumn.Contrast this to what happens if you explictly set the type of
cto a generic type (but with an unknwon cell type):In this case the compiler emits a type mismatch error:
This is just like in scala when
cis typed as TableColumn[Foo, _]