JavaFX 2 and setCellValueFactory () in Scala?

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 for me it looks like this:

<TableView fx:id="test"> <columns> <TableColumn prefWidth="75.0" text="Message" /> </columns> </TableView> 

And the 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 is that the setCellValueFactory line calls:

error: class type is required, but javafx.scene.control.cell.PropertyValueFactory [com.myapp.models.Foo, _] found c.setCellValueFactory (new PropertyValueFactoryFoo, _)

I do not understand how I should use this method. If I replaced _ with String , I get:

error: type of discrepancy; 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 is type? 0 c.setCellValueFactory (new PropertyValueFactoryFoo, String)

I can confirm that everything works fine if I delete the setCellValueFactory row - I just don't see any content in the table - just empty rows as expected.

+4
source share
2 answers

TL DR: In java, it is easy to disable type safety by type parameters. Scala will not allow you to do this without performing an explicit cast. In addition, using Table.getColumns to retrieve a column means that we are losing the type of cell. Solutions: TableColumn instance to the appropriate type or create the column instance manually.

The first problem is the signature of Table.getColumns : it returns a ObservableList[TableColumn[S,_]] (see http://docs.oracle.com/javafx/2/api/javafx/scene/control/TableView.html# getColumns ()). So your c val is introduced as TableColumn[Foo,_] . This means that you are losing the type of cell content (indicated by the parameter of the second type).

The only solution here is to enter column ( c ) correctly with cast:

 val c = test.getColumns.get(0).asInstanceOf[TableColumn[Foo, String]] c.setCellValueFactory(new PropertyValueFactory[Foo, String]("message")) 

This is quite logical: you have a list of columns in which each column can contain objects of a different type. We would encounter the same dilemma with a scala List containing objects of different types, all unknown a priori: only listing (or matching a type that is hidden cast) will allow you to return a specific type at compile time.

Also note that in many JavaFx examples on the Internet, people do not retrieve columns through Table.getColumns . Instead, they copy them manually, and then click setCellValueFactory right after it. Performing the same task will help solve your problem (without any need for a cast), since you yourself will specify parameters like:

 val c = new TableColumn[Foo, String] // See ma, no cast! c.setCellValueFactory(new PropertyValueFactory[Foo, String]("message")) 

Now you can look at some Java examples and notice that they do not provide the any parameter when creating their TableColumn :

 TableColumn c = new TableColumn(); c.setCellValueFactory( new PropertyValueFactory<Foo,String>("name")); 

And indeed, it compiles. How so? Well, the sad truth is that the above code is not safer than casting, as it relies on Java backward compatibility for classes that support pre-generics. In other words, since c is typed as simply a TableColumn , rather than a TableColumn<?, String> , for example, the java compiler considers it to be not a general class and does not perform type checking on parameters of type TableColumn . Compare this to what happens if you explicitly set type c to a generic type (but with a cell type of unknwon):

 TableColumn<Foo, ?> name = new TableColumn("Name"); name.setCellValueFactory( new PropertyValueFactory<Room,String>("name")); 

In this case, the compiler throws a type mismatch error:

 error: method setCellValueFactory in class TableColumn<S,T> cannot be applied to given types; ... 

It's like in scala when c is typed like TableColumn [Foo, _]

+5
source

I suggest using the long PropertyValueFactory form:
(Sorry, I'm not familiar with Scala, so I wrote in Java)

  c.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Foo, String>, ObservableValue<String>>() { public ObservableValue<String> call(TableColumn.CellDataFeatures<Foo, String> f) { // f.getValue() returns the Foo instance for a particular TableView row return f.getValue().messageProperty(); // or return new SimpleStringProperty(p.getValue().getMessage()); // if you omit messageProperty() in Foo model class. // However omitting it, causes the property listener could not be attached resulting // unable to refresh (synchronize) tableview row item value when the backing // Foo message value is changed. } }); 

Please note that your Foo model should look like:

 public class Foo { private SimpleStringProperty message; public SimpleStringProperty messageProperty() { return message; } public String getMessage() { return message.get(); } public void setMessage(String message) { this.message.set(message); } } 
+1
source

Source: https://habr.com/ru/post/1438288/


All Articles