-1

I have several classes that all inherit from one super class that need to populate several TableViews related to their class.

The super class is abstract and some of the getters and setters are final but still contains data needed to populate the cells.

Writing a new Callback class for each and every column is doable, but I'm looking for a way to implements this.

sample code

class SuperClass
{
  protected String name;
  protected double value;
  public final void setName(String name)
  {
    this.name = name;
  }
  public final void getName()
  {
    return this.name;
  }
  public final void setValue(double value)
  {
    this.value = value;
  }
  public double getValue()
  {
    return this.value;
  }
}
class SubClass1 extends SuperClass
{
  private int id;
  public void setId(int id)
  {
    this.id = id;
  }
  public int getId()
  {
    return this.id;
  }
}
class SubClass2 extends SuperClass
{
  private String location;
  public void setLocation(String location)
  {
    this.location = location;
  }
}
class SubClass3 extends SuperClass
{
  private ObservableMap<SuperClass> map;
  public ObservableMap<SuperClass> map()
  {
    return this.map;
  }
}

TableView

TableColumn<SubClass1, Integer> tc1_id;
TableColumn<SubClass1, String> tc1_name;
TableColumn<SubClass1, Double> tc1_value;
TableColumn<SubClass2, String> tc2_loc;
TableColumn<SubClass2, String> tc2_name;
TableColumn<SubClass2, Double> tc2_value;
TableColumn<SubClass3, String> tc3_name;
TableColumn<SubClass3, Double> tc3_value;

Here's a reference of what I was going to do... Accessing Subclass properties in a JavaFX TableView ObservableArrayList

But just with the sample code, I'm basically rewriting 2 methods, 3 times each... and there's a bit more than that in the actual program. (Just a smidge more)

Community
  • 1
  • 1
David Fisher
  • 282
  • 2
  • 13
  • You can't use all those columns in the same `TableView`. A `TableColumn` can only be added to a `TableView`, so the first type parameter must be the same for all your columns. – James_D Jun 17 '20 at 18:04
  • tc1 refers to table 1 -> column X tc2 refers to table 2 -> column X tc3 refers to table 3 -> column X – David Fisher Jun 17 '20 at 20:27
  • So each subclass corresponds to a different table? In that case, I don't understand what you're asking. – James_D Jun 17 '20 at 20:28
  • That is correct. "several TableView" – David Fisher Jun 17 '20 at 20:30
  • So, what are you asking? – James_D Jun 17 '20 at 20:30
  • A way to not have to write out 60+ anonymous `CallBack` classes for each `TableView` for each column that has their properties derived from the `SuperClass` – David Fisher Jun 17 '20 at 20:34
  • Just write a single function to set the `cellValueFactory` on a table column, and call it for each column. `Callback` is a `FunctionalInterface`, so you can reduce code further by using lambdas. Is there a reason you're not using JavaFX Properties in your classes? – James_D Jun 17 '20 at 20:37
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/216166/discussion-between-david-fisher-and-james-d). – David Fisher Jun 17 '20 at 20:38
  • 1
    explain your problem here (best with a [mcve]) and java naming conventions please .. Note that this site is _not_ a helpdesk, but a community effort to build a knowledge base, incomplete/vague/unclear questions don't help in that effort – kleopatra Jun 17 '20 at 20:42

1 Answers1

1

I think you are just asking how to reduce the amount of code you have to write. The solution is just the same as any such question: write a method that performs the repetitive part, and parametrize it with the parts that vary. So in this case, you just need to write a generic utility method to generate your table columns, taking the title of the column and the function that produces the property the cell value factory needs.

E.g. you could do something like

private <S,T> TableColumn<S,T> createColumn(String title, Function<S, Property<T>> prop) {
    TableColumn<S,T> column = new TableColumn<>(title);
    column.setCellValueFactory(cellData -> prop.apply(cellData.getValue()));
    return column ;
}

and then if your model classes use JavaFX properties, all you need is

TableColumn<SubClass1, Number> tc1Id = createColumn("Id", SubClass1::idProperty);

etc.

If you are not using JavaFX properties (which is the recommended approach), you can still do

TableColumn<SubClass2, String> tc2Loc = 
    createColumn("Location", item -> new SimpleStringProperty(item.getLocation()));

or just create a method that accepts a Function<S,T> instead of a Function<S,Property<T>>.

James_D
  • 201,275
  • 16
  • 291
  • 322
  • After converting SuperClass's fields to Property type and adding in the methods StringProperty nameProperty(), I still have an issue with `TableView table; TableColumn tableColumn = createColumn("Name", SuperClass::nameProperty); table.setItems(ObservableList);` though I know in your first answer you mentioned something about not being able to have multiple types of items in a `TableView` but the `TableColumn`s are only referencing `Properties` from the `SuperClass` – David Fisher Jun 18 '20 at 15:40
  • What is the issue, specifically? – James_D Jun 18 '20 at 15:56
  • `tableColumn.setCellValueFactory(PropertyValueFactory("name");` generates TableColumn cannot be cast to javafx.scene.control.TableColumn$CellDataFeatures --- `TableColumn tableColumn = createColumn("name", SuperClass::nameProperty);` won't compile " incompatible types: inferred type does not conform to equality constraint(s)" --- signature for `SuperClass::nameProperty` is `public final StringProperty nameProperty()` – David Fisher Jun 18 '20 at 16:28
  • 1
    @DavidFisher Yes, because it should be `TableColumn tableColumn = createColumn(...);`. For a `TableColumn` the method signature is `setCellValueFactory(Callback, ObservableValue>)`. So if you make `T` a `StringProperty` you'd end up having to return an `ObservableValue`, instead of what is currently returned (a `StringProperty`, which implements `ObservableValue`). – James_D Jun 18 '20 at 16:31
  • Seems weird that I can't have a `T extend Property` But I guess that works – David Fisher Jun 18 '20 at 17:02
  • @DavidFisher why? The cell value factory has to return an observable wrapper of the value of the cell (so that it can automatically update). Why would you want the actual value displayed to be an observable wrapper of something? It's not exactly natural to have `StringProperty` as a property type (so your bean methods would be `public StringProperty getName();` `public void setName(StringProperty value);` and `public Property nameProperty()` ???). – James_D Jun 18 '20 at 17:03