The Bean Class for JavaFX Programming

      1 Comment on The Bean Class for JavaFX Programming

In my earlier article, The Bean Class for Java Programming, I presented how I want you, my students, to code a data or model class in your assignments. That style of bean class is widely used in frameworks such as Hibernate, Java Server Faces, CDI and others. There is one variant of that class that you need to know about and that is the JavaFX bean class.

JavaFX is the modern GUI framework that is the successor to Swing as it was the successor to AWT. One important characteristic of JavaFX is that the observable pattern is baked into its beans, pun intended. When a JavaFX bean is bound or connected to a JavaFX control. such as a text field, changes to the control can automatically update the bean and changes to the bean updates the control. This is an important feature in the Model View Controller pattern that JavaFX promotes.

A JavaFX bean, when properly constructed, can be a drop-in replacement for a Java bean in most instances. It is expected to adhere to all the mandatory, optional and even bonus features I described in the earlier article. Let’s begin by reviewing the fields from that article.

[pastacode lang=”java” manual=”%20%20%20%20private%20String%20isbn%3B%0A%20%20%20%20private%20String%20title%3B%0A%20%20%20%20private%20String%20author%3B%0A%20%20%20%20private%20String%20publisher%3B%0A%20%20%20%20private%20int%20pages%3B” message=”” highlight=”” provider=”manual”/]

I have added an additional field to make this bean a little more interesting. The field will represent the date that the book was published.

[pastacode lang=”java” manual=”%20%20%20%20private%20LocalDate%20datePublished%3B” message=”” highlight=”” provider=”manual”/]

To make this bean observable we must change how we declare these fields. JavaFX introduced a family of wrapper classes called property classes. These property classes exist for the primitive types, the String, List, Map, Object, Set and a catch all for any other class type. These classes come in read / write as well as read only versions. They also come in abstract and concrete classes. The abstract class is the property type (LHS) and the concrete class is the instantiation (RHS). Here is a table of the read / write property classes.

[pastacode lang=”java” manual=”Abstract%20%2F%20LHS%20Type%09%20Concrete%20%2F%20RHS%20Type%0A———————————————-%0ABooleanProperty%20%20%20%20%20%20SimpleBooleanProperty%0ADoubleProperty%20%20%20%20%20%20%20SimpleDoubleProperty%0AFloatProperty%20%20%20%20%20%20%20%20SimpleFloatProperty%0AIntegerProperty%20%20%20%20%20%20SimpleIntegerProperty%0AListProperty%3CE%3E%20%20%20%20%20%20SimpleListProperty%3CE%3E%0ALongProperty%20%20%20%20%20%20%20%20%20SimpleLongProperty%0AMapProperty%3CK%2CV%3E%20%20%20%20%20SimpleMapProperty%3CK%2CV%3E%0AObjectProperty%3CT%3E%20%20%20%20SimpleObjectProperty%3CT%3E%0ASetProperty%3CE%3E%20%20%20%20%20%20%20SimpleSetProperty%3CE%3E%0AStringProperty%20%20%20%20%20%20%20SimpleStringProperty%0A” message=”” highlight=”” provider=”manual”/]

The abstract property classes define the required methods of a property in a general way. The concrete class may override the general methods but do not add any additional methods. JavaFX has other concrete property classes that can appear on the right-hand side. The concrete classes extend the abstract class. This is an example of pure inheritance.

This means that we need to rewrite the fields as follows.

[pastacode lang=”java” manual=”%20%20%20%20private%20final%20StringProperty%20isbn%3B%0A%20%20%20%20private%20final%20StringProperty%20title%3B%0A%20%20%20%20private%20final%20StringProperty%20author%3B%0A%20%20%20%20private%20final%20StringProperty%20publisher%3B%0A%20%20%20%20private%20final%20IntegerProperty%20pages%3B%0A%20%20%20%20private%20final%20ObjectProperty%3CLocalDate%3E%20datePublished%3B%0A” message=”” highlight=”” provider=”manual”/]

The next change that we make from the original bean is that we now must have a constructor. These property fields are null references to abstract classes so you must instantiate them. If you do not plan to have a non-default constructor then all you need is something like this:

[pastacode lang=”java” manual=”%20%20%20%20public%20BookFX()%20%7B%0A%20%20%20%20%20%20%20%20this.isbn%20%3D%20new%20SimpleStringProperty(%22%22)%3B%0A%20%20%20%20%20%20%20%20this.title%20%3D%20new%20SimpleStringProperty(%22%22)%3B%0A%20%20%20%20%20%20%20%20this.author%20%3D%20new%20SimpleStringProperty(%22%22)%3B%0A%20%20%20%20%20%20%20%20this.publisher%20%3D%20new%20SimpleStringProperty(%22%22)%3B%0A%20%20%20%20%20%20%20%20this.pages%20%3D%20new%20SimpleIntegerProperty(0)%3B%0A%20%20%20%20%20%20%20%20this.datePublished%20%3D%20new%20SimpleObjectProperty%3C%3E(LocalDate.now())%3B%0A%20%20%20%20%7D%0A” message=”” highlight=”” provider=”manual”/]

Each of the references to an abstract class is instantiated with a concrete class. The concrete classes all accept an appropriate value of the underlying type in their constructor.

If we need to have both a default and non-default constructor they can be written like this:

[pastacode lang=”java” manual=”%20%20%20%20public%20BookFX()%20%7B%0A%20%20%20%20%20%20%20%20this(%22%22%2C%20%22%22%2C%20%22%22%2C%20%22%22%2C%200%2C%20LocalDate.now())%3B%0A%20%20%20%20%7D%0A%20%20%20%0A%20%20%20%20public%20BookFX(final%20String%20isbn%2C%20final%20String%20title%2C%20final%20String%20author%2C%20final%20String%20publisher%2C%20final%20int%20pages%2C%20final%20LocalDate%20datePublished)%20%7B%0A%20%20%20%20%20%20%20%20this.isbn%20%3D%20new%20SimpleStringProperty(isbn)%3B%0A%20%20%20%20%20%20%20%20this.title%20%3D%20new%20SimpleStringProperty(title)%3B%0A%20%20%20%20%20%20%20%20this.author%20%3D%20new%20SimpleStringProperty(author)%3B%0A%20%20%20%20%20%20%20%20this.publisher%20%3D%20new%20SimpleStringProperty(publisher)%3B%0A%20%20%20%20%20%20%20%20this.pages%20%3D%20new%20SimpleIntegerProperty(pages)%3B%0A%20%20%20%20%20%20%20%20this.datePublished%20%3D%20new%20SimpleObjectProperty%3C%3E(datePublished)%3B%0A%20%20%20%20%7D%0A” message=”” highlight=”” provider=”manual”/]

The default constructor uses Java’s ability to have a constructor call another constructor by overloading. The non-default constructor can now be used to create an instance of the object and at the same time this class has the required default constructor.

The setters and getters must also be modified. These methods are expected to get or set the value inside the property object and not the property itself. This is easy to do as property classes have a get and set method to handle this. Here are the setters and getters.

[pastacode lang=”java” manual=”%20%20%20%20public%20final%20String%20getIsbn()%20%7B%0A%20%20%20%20%20%20%20%20return%20isbn.get()%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20void%20setIsbn(final%20String%20isbn)%20%7B%0A%20%20%20%20%20%20%20%20this.isbn.set(isbn)%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20final%20String%20getTitle()%20%7B%0A%20%20%20%20%20%20%20%20return%20title.get()%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20void%20setTitle(final%20String%20title)%20%7B%0A%20%20%20%20%20%20%20%20this.title.set(title)%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20String%20getAuthor()%20%7B%0A%20%20%20%20%20%20%20%20return%20author.get()%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20void%20setAuthor(final%20String%20author)%20%7B%0A%20%20%20%20%20%20%20%20this.author.set(author)%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20final%20String%20getPublisher()%20%7B%0A%20%20%20%20%20%20%20%20return%20publisher.get()%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20void%20setPublisher(final%20String%20publisher)%20%7B%0A%20%20%20%20%20%20%20%20this.publisher.set(publisher)%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20final%20int%20getPages()%20%7B%0A%20%20%20%20%20%20%20%20return%20pages.get()%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20void%20setPages(final%20int%20pages)%20%7B%0A%20%20%20%20%20%20%20%20this.pages.set(pages)%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20final%20LocalDate%20getDatePublished()%20%7B%0A%20%20%20%20%20%20%20%20return%20datePublished.get()%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20void%20setDatePublished(final%20LocalDate%20datePublished)%20%7B%0A%20%20%20%20%20%20%20%20this.datePublished.set(datePublished)%3B%0A%20%20%20%20%7D” message=”” highlight=”” provider=”manual”/]

With this syntax we now have a drop in replacement for standard Java beans as described in my first article. This is not complete yet. We are using property classes to achieve an observable pattern. To accomplish this JavaFX controls must be bound or connected to the individual fields. The controls need a reference to the property object. Only the equivalent of a getter is required. There is no naming convention because we write out the method name when we do the binding. The most common naming approach is to add the word Property to the variable name. Here are the getters for the properties.

[pastacode lang=”java” manual=”%20%20%20%20public%20final%20StringProperty%20isbnProperty()%20%7B%0A%20%20%20%20%20%20%20%20return%20isbn%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20final%20StringProperty%20titleProperty()%20%7B%0A%20%20%20%20%20%20%20%20return%20title%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20final%20StringProperty%20authorProperty()%20%7B%0A%20%20%20%20%20%20%20%20return%20author%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20final%20StringProperty%20publisherProperty()%20%7B%0A%20%20%20%20%20%20%20%20return%20publisher%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20final%20IntegerProperty%20pagesProperty()%20%7B%0A%20%20%20%20%20%20%20%20return%20pages%3B%0A%20%20%20%20%7D%0A%20%20%20%20public%20final%20ObjectProperty%3CLocalDate%3E%20datePublishedProprty()%20%7B%0A%20%20%20%20%20%20%20%20return%20datePublished%3B%0A%20%20%20%20%7D” message=”” highlight=”” provider=”manual”/]

There is not a setter for a property because once created and bound to a control it must not be replaced. Only its contained value can be altered.

The hashCode, equals, toString and compareTo methods need to be modified to use the get and set methods of the properties.

[pastacode lang=”java” manual=”%20%20%20%40Override%0A%20%20%20%20public%20String%20toString()%20%7B%0A%20%20%20%20%20%20%20%20return%20%22Book%7B%22%20%2B%20%22isbn%3D%22%20%2B%20isbn.get()%20%2B%20%22%2C%20title%3D%22%20%2B%20title.get()%20%2B%20%22%2C%20author%3D%22%20%2B%20author.get()%20%2B%20%22%2C%20publisher%3D%22%20%2B%20publisher.get()%20%2B%20%22%2C%20pages%3D%22%20%2B%20pages.get()%20%2B%20%22%2C%20datePublished%3D%22%20%2B%20datePublished.get()%20%2B%20’%7D’%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%40Override%0A%20%20%20%20public%20int%20hashCode()%20%7B%0A%20%20%20%20%20%20%20%20int%20hash%20%3D%207%3B%0A%20%20%20%20%20%20%20%20hash%20%3D%2061%20*%20hash%20%2B%20Objects.hashCode(this.isbn.get())%3B%0A%20%20%20%20%20%20%20%20hash%20%3D%2061%20*%20hash%20%2B%20Objects.hashCode(this.title.get())%3B%0A%20%20%20%20%20%20%20%20hash%20%3D%2061%20*%20hash%20%2B%20Objects.hashCode(this.author.get())%3B%0A%20%20%20%20%20%20%20%20hash%20%3D%2061%20*%20hash%20%2B%20Objects.hashCode(this.publisher.get())%3B%0A%20%20%20%20%20%20%20%20hash%20%3D%2061%20*%20hash%20%2B%20this.pages.get()%3B%0A%20%20%20%20%20%20%20%20hash%20%3D%2061%20*%20hash%20%2B%20Objects.hashCode(this.datePublished.get())%3B%0A%0A%20%20%20%20%20%20%20%20return%20hash%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%40Override%0A%20%20%20%20public%20boolean%20equals(Object%20obj)%20%7B%0A%20%20%20%20%20%20%20%20if%20(this%20%3D%3D%20obj)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20true%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(obj%20%3D%3D%20null)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(getClass()%20!%3D%20obj.getClass())%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20final%20BookFX%20other%20%3D%20(BookFX)%20obj%3B%0A%20%20%20%20%20%20%20%20if%20(this.pages.get()%20!%3D%20other.pages.get())%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(!Objects.equals(this.isbn.get()%2C%20other.isbn.get()))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(!Objects.equals(this.title.get()%2C%20other.title.get()))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(!Objects.equals(this.author.get()%2C%20other.author.get()))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(!Objects.equals(this.publisher.get()%2C%20other.publisher.get()))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(!Objects.equals(this.datePublished.get()%2C%20other.datePublished.get()))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20true%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%40Override%0A%20%20%20%20public%20int%20compareTo(BookFX%20book)%20%7B%0A%20%20%20%20%20%20%20%20return%20this.title.get().compareTo(book.title.get())%3B%0A%20%20%20%20%7D” message=”” highlight=”” provider=”manual”/]

The only change required to our Comparator class is to declare the beans as JavaFX beans rather than Java beans.

[pastacode lang=”java” manual=”public%20class%20BookPageComparatorFX%20implements%20Comparator%3CBookFX%3E%20%7B%0A%20%20%20%20%40Override%0A%20%20%20%20public%20int%20compare(BookFX%20book1%2C%20BookFX%20book2)%20%7B%0A%20%20%20%20%20%20%20%20return%20book1.getPages()%20-%20book2.getPages()%3B%0A%20%20%20%20%7D%0A%7D” message=”” highlight=”” provider=”manual”/]

Put all together this is what our JavaFX bean looks like.

[pastacode lang=”java” manual=”import%20java.io.Serializable%3B%0Aimport%20java.time.LocalDate%3B%0Aimport%20java.util.Objects%3B%0Aimport%20javafx.beans.property.IntegerProperty%3B%0Aimport%20javafx.beans.property.ObjectProperty%3B%0Aimport%20javafx.beans.property.SimpleIntegerProperty%3B%0Aimport%20javafx.beans.property.SimpleObjectProperty%3B%0Aimport%20javafx.beans.property.SimpleStringProperty%3B%0Aimport%20javafx.beans.property.StringProperty%3B%0A%0Apublic%20class%20BookFX%20implements%20Serializable%2C%20Comparable%3CBookFX%3E%20%7B%0A%0A%20%20%20%20private%20final%20StringProperty%20isbn%3B%0A%20%20%20%20private%20final%20StringProperty%20title%3B%0A%20%20%20%20private%20final%20StringProperty%20author%3B%0A%20%20%20%20private%20final%20StringProperty%20publisher%3B%0A%20%20%20%20private%20final%20IntegerProperty%20pages%3B%0A%20%20%20%20private%20final%20ObjectProperty%3CLocalDate%3E%20datePublished%3B%0A%0A%20%20%20%20%2F**%0A%20%20%20%20%20*%20Default%20constructor%0A%20%20%20%20%20*%2F%0A%20%20%20%20public%20BookFX()%20%7B%0A%20%20%20%20%20%20%20%20this(%22%22%2C%20%22%22%2C%20%22%22%2C%20%22%22%2C%200%2C%20LocalDate.now())%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%2F**%0A%20%20%20%20%20*%20Non-default%20constructor%0A%20%20%20%20%20*%0A%20%20%20%20%20*%20%40param%20isbn%0A%20%20%20%20%20*%20%40param%20title%0A%20%20%20%20%20*%20%40param%20author%0A%20%20%20%20%20*%20%40param%20publisher%0A%20%20%20%20%20*%20%40param%20pages%0A%20%20%20%20%20*%20%40param%20datePublished%0A%20%20%20%20%20*%2F%0A%20%20%20%20public%20BookFX(final%20String%20isbn%2C%20final%20String%20title%2C%20final%20String%20author%2C%20final%20String%20publisher%2C%20final%20int%20pages%2C%20final%20LocalDate%20datePublished)%20%7B%0A%20%20%20%20%20%20%20%20this.isbn%20%3D%20new%20SimpleStringProperty(isbn)%3B%0A%20%20%20%20%20%20%20%20this.title%20%3D%20new%20SimpleStringProperty(title)%3B%0A%20%20%20%20%20%20%20%20this.author%20%3D%20new%20SimpleStringProperty(author)%3B%0A%20%20%20%20%20%20%20%20this.publisher%20%3D%20new%20SimpleStringProperty(publisher)%3B%0A%20%20%20%20%20%20%20%20this.pages%20%3D%20new%20SimpleIntegerProperty(pages)%3B%0A%20%20%20%20%20%20%20%20this.datePublished%20%3D%20new%20SimpleObjectProperty%3C%3E(datePublished)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20final%20String%20getIsbn()%20%7B%0A%20%20%20%20%20%20%20%20return%20isbn.get()%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20void%20setIsbn(final%20String%20isbn)%20%7B%0A%20%20%20%20%20%20%20%20this.isbn.set(isbn)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20final%20StringProperty%20isbnProperty()%20%7B%0A%20%20%20%20%20%20%20%20return%20isbn%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20final%20String%20getTitle()%20%7B%0A%20%20%20%20%20%20%20%20return%20title.get()%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20void%20setTitle(final%20String%20title)%20%7B%0A%20%20%20%20%20%20%20%20this.title.set(title)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20final%20StringProperty%20titleProperty()%20%7B%0A%20%20%20%20%20%20%20%20return%20title%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20String%20getAuthor()%20%7B%0A%20%20%20%20%20%20%20%20return%20author.get()%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20void%20setAuthor(final%20String%20author)%20%7B%0A%20%20%20%20%20%20%20%20this.author.set(author)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20final%20StringProperty%20authorProperty()%20%7B%0A%20%20%20%20%20%20%20%20return%20author%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20final%20String%20getPublisher()%20%7B%0A%20%20%20%20%20%20%20%20return%20publisher.get()%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20void%20setPublisher(final%20String%20publisher)%20%7B%0A%20%20%20%20%20%20%20%20this.publisher.set(publisher)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20final%20StringProperty%20publisherProperty()%20%7B%0A%20%20%20%20%20%20%20%20return%20publisher%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20final%20int%20getPages()%20%7B%0A%20%20%20%20%20%20%20%20return%20pages.get()%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20void%20setPages(final%20int%20pages)%20%7B%0A%20%20%20%20%20%20%20%20this.pages.set(pages)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20final%20IntegerProperty%20pagesProperty()%20%7B%0A%20%20%20%20%20%20%20%20return%20pages%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20final%20LocalDate%20getDatePublished()%20%7B%0A%20%20%20%20%20%20%20%20return%20datePublished.get()%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20void%20setDatePublished(final%20LocalDate%20datePublished)%20%7B%0A%20%20%20%20%20%20%20%20this.datePublished.set(datePublished)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20public%20final%20ObjectProperty%3CLocalDate%3E%20datePublishedProprty()%20%7B%0A%20%20%20%20%20%20%20%20return%20datePublished%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%40Override%0A%20%20%20%20public%20String%20toString()%20%7B%0A%20%20%20%20%20%20%20%20return%20%22Book%7B%22%20%2B%20%22isbn%3D%22%20%2B%20isbn.get()%20%2B%20%22%2C%20title%3D%22%20%2B%20title.get()%20%2B%20%22%2C%20author%3D%22%20%2B%20author.get()%20%2B%20%22%2C%20publisher%3D%22%20%2B%20publisher.get()%20%2B%20%22%2C%20pages%3D%22%20%2B%20pages.get()%20%2B%20%22%2C%20datePublished%3D%22%20%2B%20datePublished.get()%20%2B%20’%7D’%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%40Override%0A%20%20%20%20public%20int%20hashCode()%20%7B%0A%20%20%20%20%20%20%20%20int%20hash%20%3D%207%3B%0A%20%20%20%20%20%20%20%20hash%20%3D%2061%20*%20hash%20%2B%20Objects.hashCode(this.isbn.get())%3B%0A%20%20%20%20%20%20%20%20hash%20%3D%2061%20*%20hash%20%2B%20Objects.hashCode(this.title.get())%3B%0A%20%20%20%20%20%20%20%20hash%20%3D%2061%20*%20hash%20%2B%20Objects.hashCode(this.author.get())%3B%0A%20%20%20%20%20%20%20%20hash%20%3D%2061%20*%20hash%20%2B%20Objects.hashCode(this.publisher.get())%3B%0A%20%20%20%20%20%20%20%20hash%20%3D%2061%20*%20hash%20%2B%20this.pages.get()%3B%0A%20%20%20%20%20%20%20%20hash%20%3D%2061%20*%20hash%20%2B%20Objects.hashCode(this.datePublished.get())%3B%0A%0A%20%20%20%20%20%20%20%20return%20hash%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%40Override%0A%20%20%20%20public%20boolean%20equals(Object%20obj)%20%7B%0A%20%20%20%20%20%20%20%20if%20(this%20%3D%3D%20obj)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20true%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(obj%20%3D%3D%20null)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(getClass()%20!%3D%20obj.getClass())%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20final%20BookFX%20other%20%3D%20(BookFX)%20obj%3B%0A%20%20%20%20%20%20%20%20if%20(this.pages.get()%20!%3D%20other.pages.get())%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(!Objects.equals(this.isbn.get()%2C%20other.isbn.get()))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(!Objects.equals(this.title.get()%2C%20other.title.get()))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(!Objects.equals(this.author.get()%2C%20other.author.get()))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(!Objects.equals(this.publisher.get()%2C%20other.publisher.get()))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(!Objects.equals(this.datePublished.get()%2C%20other.datePublished.get()))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20true%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%2F**%0A%20%20%20%20%20*%20Natural%20comparison%20method%20for%20Title%0A%20%20%20%20%20*%0A%20%20%20%20%20*%20%40param%20book%0A%20%20%20%20%20*%20%40return%20negative%20value%2C%200%2C%20or%20positive%20value%0A%20%20%20%20%20*%2F%0A%20%20%20%20%40Override%0A%20%20%20%20public%20int%20compareTo(BookFX%20book)%20%7B%0A%20%20%20%20%20%20%20%20return%20this.title.get().compareTo(book.title.get())%3B%0A%20%20%20%20%7D%0A%7D%0A” message=”” highlight=”” provider=”manual”/]

The test program for this bean is almost identical to the original BeanTester but with the LocalDate added and used in a Comparator function.

[pastacode lang=”java” manual=”%2F%2F%20Required%20for%20Arrays.sort%0Aimport%20java.time.LocalDate%3B%0Aimport%20java.util.Arrays%3B%0A%2F%2F%20Required%20by%20the%20Comparator%20function%0Aimport%20static%20java.util.Comparator.comparing%3B%0A%0Apublic%20class%20BeanTesterFX%20%7B%0A%0A%20%20%20%20%2F**%0A%20%20%20%20%20*%20Here%20is%20where%20I%20am%20testing%20my%20comparisons%0A%20%20%20%20%20*%0A%20%20%20%20%20*%2F%0A%20%20%20%20public%20void%20perform()%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20Lets%20create%20four%20books%0A%20%20%20%20%20%20%20%20BookFX%20b0%20%3D%20new%20BookFX(%22200%22%2C%20%22Xenon%22%2C%20%22Hamilton%22%2C%20%22Harcourt%22%2C%2099%2C%20LocalDate.of(2017%2C%202%2C%204))%3B%0A%20%20%20%20%20%20%20%20BookFX%20b1%20%3D%20new%20BookFX(%22500%22%2C%20%22Boron%22%2C%20%22Bradbury%22%2C%20%22Prentice%22%2C%20108%2C%20LocalDate.of(2018%2C%205%2C%2023))%3B%0A%20%20%20%20%20%20%20%20BookFX%20b2%20%3D%20new%20BookFX(%22300%22%2C%20%22Radon%22%2C%20%22Heinlein%22%2C%20%22Thompson%22%2C%2098%2C%20LocalDate.of(2015%2C%208%2C%2030))%3B%0A%20%20%20%20%20%20%20%20BookFX%20b3%20%3D%20new%20BookFX(%22404%22%2C%20%22Argon%22%2C%20%22Campbell%22%2C%20%22Hachette%22%2C%20102%2C%20LocalDate.of(2012%2C%2011%2C%2015))%3B%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Using%20Comparable%20to%20compare%20two%20books%0A%20%20%20%20%20%20%20%20System.out.println(%22Value%20returned%20by%20Comparable%22)%3B%0A%20%20%20%20%20%20%20%20System.out.println(b0.getTitle()%20%2B%20%22%20compared%20to%20%22%20%2B%20b1.getTitle()%20%2B%20%22%3A%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2B%20b0.compareTo(b1))%3B%0A%20%20%20%20%20%20%20%20System.out.println()%3B%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Using%20Comparator%20to%20compare%20two%20books%0A%20%20%20%20%20%20%20%20System.out.println(%22Value%20returned%20by%20Comparator%22)%3B%0A%20%20%20%20%20%20%20%20BookPageComparatorFX%20bookPageComparatorFX%20%3D%20new%20BookPageComparatorFX()%3B%0A%20%20%20%20%20%20%20%20System.out.println(b0.getPages()%20%2B%20%22%20compared%20to%20%22%20%2B%20b1.getPages()%20%2B%20%22%3A%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2B%20bookPageComparatorFX.compare(b0%2C%20b1))%3B%0A%20%20%20%20%20%20%20%20System.out.println()%3B%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Create%20an%20array%20we%20can%20sort%0A%20%20%20%20%20%20%20%20BookFX%5B%5D%20myBooks%20%3D%20new%20BookFX%5B4%5D%3B%0A%20%20%20%20%20%20%20%20myBooks%5B0%5D%20%3D%20b0%3B%0A%20%20%20%20%20%20%20%20myBooks%5B1%5D%20%3D%20b1%3B%0A%20%20%20%20%20%20%20%20myBooks%5B2%5D%20%3D%20b2%3B%0A%20%20%20%20%20%20%20%20myBooks%5B3%5D%20%3D%20b3%3B%0A%20%20%20%20%20%20%20%20System.out.println(%22Unsorted%22)%3B%0A%20%20%20%20%20%20%20%20displayBooks(myBooks)%3B%0A%0A%20%20%20%20%20%20%20%20System.out.println(%22Sorted%20with%20Comparable%20Interface%20on%20Title%22)%3B%0A%20%20%20%20%20%20%20%20Arrays.sort(myBooks)%3B%20%2F%2F%20uses%20the%20Comparable%20compareTo%20in%20the%20bean%0A%20%20%20%20%20%20%20%20displayBooks(myBooks)%3B%0A%0A%20%20%20%20%20%20%20%20System.out.println(%22Sorted%20with%20Comparable%20Object%20on%20Pages%22)%3B%0A%20%20%20%20%20%20%20%20Arrays.sort(myBooks%2C%20bookPageComparatorFX)%3B%20%2F%2F%20uses%20the%20Comparator%20object%0A%20%20%20%20%20%20%20%20displayBooks(myBooks)%3B%0A%0A%20%20%20%20%20%20%20%20System.out.println(%22Sorted%20with%20Comparable%20lambda%20expression%20on%20Publishers%22)%3B%0A%20%20%20%20%20%20%20%20Arrays.sort(myBooks%2C%20(s1%2C%20s2)%20-%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20s1.getPublisher().compareTo(s2.getPublisher())%3B%0A%20%20%20%20%20%20%20%20%7D)%3B%20%2F%2F%20uses%20the%20Comparator%20lambda%0A%20%20%20%20%20%20%20%20displayBooks(myBooks)%3B%0A%0A%20%20%20%20%20%20%20%20System.out.println(%22Sorted%20with%20Comparable%20lambda%20functions%20based%20on%20ISBN%22)%3B%0A%20%20%20%20%20%20%20%20Arrays.sort(myBooks%2C%20comparing(BookFX%3A%3AgetIsbn))%3B%20%2F%2F%20Comparable%20function%0A%20%20%20%20%20%20%20%20displayBooks(myBooks)%3B%0A%0A%20%20%20%20%20%20%20%20System.out.println(%22Sorted%20with%20Comparable%20lambda%20functions%20based%20on%20Date%20Published%22)%3B%0A%20%20%20%20%20%20%20%20Arrays.sort(myBooks%2C%20comparing(BookFX%3A%3AgetDatePublished))%3B%20%2F%2F%20Comparable%20function%0A%20%20%20%20%20%20%20%20displayBooks(myBooks)%3B%0A%0A%20%20%20%20%7D%0A%0A%20%20%20%20%2F**%0A%20%20%20%20%20*%20Print%20the%20contents%20of%20each%20Book%20object%20in%20the%20array%0A%20%20%20%20%20*%0A%20%20%20%20%20*%20%40param%20theBooks%0A%20%20%20%20%20*%2F%0A%20%20%20%20private%20void%20displayBooks(BookFX%5B%5D%20theBooks)%20%7B%0A%20%20%20%20%20%20%20%20for%20(BookFX%20b%20%3A%20theBooks)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20System.out.print(b.getIsbn()%20%2B%20%22%5Ct%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20System.out.print(b.getTitle()%20%2B%20%22%5Ct%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20System.out.print(b.getAuthor()%20%2B%20%22%5Ct%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20System.out.print(b.getPublisher()%20%2B%20%22%5Ct%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20System.out.println(b.getPages()%20%2B%20%22%5Ct%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20System.out.println(b.getDatePublished())%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20System.out.println()%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%2F**%0A%20%20%20%20%20*%20Where%20it%20all%20begins%0A%20%20%20%20%20*%0A%20%20%20%20%20*%20%40param%20args%0A%20%20%20%20%20*%2F%0A%20%20%20%20public%20static%20void%20main(String%5B%5D%20args)%20%7B%0A%20%20%20%20%20%20%20%20BeanTesterFX%20bt%20%3D%20new%20BeanTesterFX()%3B%0A%20%20%20%20%20%20%20%20bt.perform()%3B%0A%20%20%20%20%20%20%20%20System.exit(0)%3B%0A%20%20%20%20%7D%0A%7D%0A” message=”” highlight=”” provider=”manual”/]

JavaFX does require that you use JavaFX beans as described here. They are only required if you intend to take advantage of binding to controls. If binding is not required then an ordinary Java bean should be used.

To summarize these two articles, if you are not using JavaFX then I expect you to use the Java bean class for data and models in the work you submit to me. In a JavaFX application I expect you to use the JavaFX bean whenever binding is required.

1 thought on “The Bean Class for JavaFX Programming

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.