Search code examples
javajavafxtablecolumn

Is there some use case addressable only by PropertyValueFactory?


Below are three ways to assign a CellValueFactory to a TableColumn in JavaFX. The first one uses an anonymous class, the second one a lambda and the third one a PropertyValueFactory .

My question is about the third way, using a PropertyValueFactory.

Having examined the source code:

PropertyValueFactory

I can't see what advantage is conferred by using this class.

In the source code of PropertyValueFactory, there is a note that they "try to improve performance in large tables" by caching a 'PropertyReference' member variable. In connection with this note, a reference to RT-13937 is mentioned. I cannot find this RT online anymore.

The following things are confused in my mind.

Is there some specific use case that is addressed by using the PropertyValueFactory technique that cannot be addressed using either of the other two techniques?

Is the caching of the PropertyReference member variable meant to address the slowness of using the PropertyValueFactory class in large tables, that slowness being caused by that class's use of reflection, or is 'PropertyValueFactory' class itself meant to address the slowness of large tables generally?

 /**
   First using an anonymous class
    */

        TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name");

         firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
             public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
                 // p.getValue() returns the Person instance for a particular TableView row
                 return p.getValue().firstNameProperty();
             }
          });


    /**Second using lambda
    */


        TableColumn<Person, String> firstNameCol =new TableColumn<>("First Name");

        firstNameCol.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty());


    /**Finally using PropertyValue
    */
        TableColumn<Person, String> firstNameCol =new TableColumn<>("First Name");

        firstNameCol.setCellValueFactory(new PropertyValueFactory<>("firstName"));

Solution

  • In your use case, there isn't any significant differences using PropertyValueFactory over using lambda/anonymous classes.

    As far as I know these are the reasons for using PropertyValueFactory:

    1. It's shorter and supposedly more convenient, though I find using lambda isn't that much longer either.
    2. It has caching, like what you have mentioned, but it is for non-JavaFX properties (i.e. using reflections).

    Therefore if you are looking for use-case for PropertyValueFactory, then the most fitting use-case is when your properties are normal POJO properties.

    Consider this:

    public class Person {
        private String firstName;
        private String lastName;
    
        public final String getFirstName() { return firstName; }
        public final void setFirstName(String firstName) { this.firstName = firstName; }
    
        public final String getLastName() { return lastName; }
        public final void setLastName(String lastName) { this.lastName = lastName; }
    }
    
    TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
    firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
        public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
            return new SimpleStringProperty(getPropertyReflectively(p.getValue(), "firstName", String.class));
        }
    });
    
    private static <T> T getPropertyReflectively(Object bean, String propertyName, Class<T> propertyType) {
        // Implementation to get property value reflectively
    }
    

    So there are two things observed:

    1. You need to write your own messy reflection stuff. It can be done, but why would want to do that when PropertyValueFactory does that for you?
    2. You are returning a new instance of StringProperty, and also doing reflection every time you scroll your big one-million-entry table.

    I believe the caching mentioned is used for observation #2. You can continue to "decipher" the codes if you want to be sure, because I definitely didn't attempt to do so.