1

I am trying to fill JavaFx TableView Columns with mock data, but I keep getting a reflection error, even though I think I'm following Bean conventions correctly:

// Data model

class SensorTableEntry {
    SensorTableEntry(Integer id, String man, String type, String addr) {
        this.id = new SimpleIntegerProperty(id);
        this.manufacturer = new SimpleStringProperty(man);
        this.type = new SimpleStringProperty(type);
        this.btAddress = new SimpleStringProperty(addr);
    }

    private IntegerProperty id;
    public Integer getId() { return idProperty().get(); }
    public void setId(Integer value) { idProperty().set(value); }
    public IntegerProperty idProperty() { return id; } 

    private StringProperty manufacturer;
    public void setManufacturer(String value) { manufacturerProperty().set(value); }
    public String getManufacturer() { return manufacturerProperty().get(); }
    public StringProperty manufacturerProperty() { return manufacturer; }

    private StringProperty type;
    public void setType(String value) { typeProperty().set(value); }
    public String getType() { return typeProperty().get(); }
    public StringProperty typeProperty() { return type; } 

    private StringProperty btAddress;
    public void setBtAddress(String value) { btAddressProperty().set(value); }
    public String getBtAddress() { return btAddressProperty().get(); }
    public StringProperty btAddressProperty() { return btAddress; } 
}

// More code before this...


// Actual table inside the controller

ObservableList<SensorTableEntry> sensorEntries = FXCollections.observableArrayList(
    new SensorTableEntry(1, "manufacturer", "type", "00:00:00:00:00:00")
);   

TableView<SensorTableEntry> table = new TableView<SensorTableEntry>();

TableColumn<SensorTableEntry,Integer> idCol = new TableColumn<SensorTableEntry,Integer>("ID");
idCol.setCellValueFactory(new PropertyValueFactory<SensorTableEntry,Integer>("id"));

TableColumn<SensorTableEntry,String> manufacturerCol = new TableColumn<SensorTableEntry,String>("Manufacturer");
manufacturerCol.setCellValueFactory(new PropertyValueFactory<SensorTableEntry,String>("manufacturer"));

TableColumn<SensorTableEntry,String> typeCol = new TableColumn<SensorTableEntry,String>("Type");
typeCol.setCellValueFactory(new PropertyValueFactory<SensorTableEntry,String>("type"));

TableColumn<SensorTableEntry,String> btAddressCol = new TableColumn<SensorTableEntry,String>("Bluetooth Address");
btAddressCol.setCellValueFactory(new PropertyValueFactory<SensorTableEntry,String>("btAddress"));

table.setItems(sensorEntries);
table.getColumns().addAll(
    idCol,
    manufacturerCol,
    typeCol,
    btAddressCol
);

pane.getChildren().add(table); 

I have checked other answers to similar questions like:

Javafx PropertyValueFactory not populating Tableview

JavaFx TableView not filling all required columns

Javafx tableview not showing data in all columns

But no matter how much I check I don't seem to find where my naming went wrong. Am I missing something?

The exception I get is:

Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.IllegalAccessException: Class sun.reflect.misc.Trampoline can not access a member of class SensorTableEntry with modifiers "public" at com.sun.javafx.property.PropertyReference.getProperty(PropertyReference.java:200)

Community
  • 1
  • 1

2 Answers2

4

Your properties must be fully accessible so their getter and their owner class must both be public.

So simply replace this:

class SensorTableEntry {

With this:

public class SensorTableEntry {
Nicolas Filotto
  • 43,537
  • 11
  • 94
  • 122
1

Since you are using JavaFX properties in your model, you can use actual implementations of the callback (with lambda expressions for brevity) and avoid reflection completely. Note that IntegerProperty implements Property<Number>, not Property<Integer>, so you will need to fix the types (see JavaFX Properties in TableView):

TableColumn<SensorTableEntry,Number> idCol = new TableColumn<SensorTableEntry,Number>("ID");
idCol.setCellValueFactory(cellData -> cellData.getValue().idProperty());

TableColumn<SensorTableEntry,String> manufacturerCol = new TableColumn<SensorTableEntry,String>("Manufacturer");
manufacturerCol.setCellValueFactory(cellData -> cellData.getValue().manufacturerProperty());

TableColumn<SensorTableEntry,String> typeCol = new TableColumn<SensorTableEntry,String>("Type");
typeCol.setCellValueFactory(cellData -> cellData.getValue().typeProperty());

TableColumn<SensorTableEntry,String> btAddressCol = new TableColumn<SensorTableEntry,String>("Bluetooth Address");
btAddressCol.setCellValueFactory(cellData -> cellData.getValue().btAddressProperty());

This is generally a much better approach: the compiler will check that the properties exist and are of the correct type, and since you are not relying on reflection to evaluate the cell values, performance will be better (probably negligibly, but nevertheless...).

One other aside: in the JavaFX property pattern, the methods for the primitive wrapper properties should use primitive types, not object wrapper types, i.e.:

class SensorTableEntry {
    SensorTableEntry(int id, String man, String type, String addr) {
        this.id = new SimpleIntegerProperty(id);
        this.manufacturer = new SimpleStringProperty(man);
        this.type = new SimpleStringProperty(type);
        this.btAddress = new SimpleStringProperty(addr);
    }

    private IntegerProperty id;
    public int getId() { return idProperty().get(); }
    public void setId(int value) { idProperty().set(value); }
    public IntegerProperty idProperty() { return id; } 

    // existing code...
}
Community
  • 1
  • 1
James_D
  • 201,275
  • 16
  • 291
  • 322
  • This also worked, but I had to change it a bit like this: **TableColumn("ID"); idCol.setCellValueFactory(cellData -> new SimpleIntegerProperty(cellData.getValue().idProperty().getValue()).asObject());** – Federico Orquera Nov 14 '16 at 16:37
  • @FedericoOrquera That won't work correctly if you make the column editable (and even if not, it's inefficient to keep creating new properties like that). As I pointed out in the answer, you should change the type of the column from `Integer` to `Number`. Again, see http://stackoverflow.com/questions/24889638/javafx-properties-in-tableview/24890425#24890425. – James_D Nov 14 '16 at 16:42