Search code examples
javainheritancejavafxtableview

JavaFX: How do I fill a table view with 2 different classes with the same super class


I have 3 classes the first one is Library Item this is the super class. The other two classes are Book and Movie. When I want to fill my table view I want to make sure the correct property is called when populating the table view. I know it is easier to just call the director and author the same for ease of use, but I want to get it working for learning purposes. I have left out packages and imports for relevance.

LibraryItem class

public abstract class LibraryItem {
    private int itemCode;
    private String title;
    private boolean availability;
    private int memberIdentifier;
    private LocalDate dateLent;

    protected LibraryItem(int itemCode, String title, boolean availability, int memberIdentifier, LocalDate dateLent) {
        this.itemCode = itemCode;
        this.title = title;
        this.availability = availability;
        this.memberIdentifier = memberIdentifier;
        this.dateLent = dateLent;
    }

    public int getItemCode() {
        return itemCode;
    }

    public String getTitle() {
        return title;
    }

    public boolean isAvailability() {
        return availability;
    }

    public void setAvailability(boolean availability) {
        this.availability = availability;
    }

    public int getMemberIdentifier() {
        return memberIdentifier;
    }

    public void setMemberIdentifier(int memberIdentifier) {
        this.memberIdentifier = memberIdentifier;
    }

    public LocalDate getDateLent() {
        return dateLent;
    }

    public void setDateLent(LocalDate dateLent) {
        this.dateLent = dateLent;
    }
}

Book class

public class Book extends LibraryItem {
    private String author;

    protected Book(int itemCode, String title, boolean isLent, int memberIdentifier, LocalDate dateLent, String author) {
        super(itemCode, title, isLent, memberIdentifier, dateLent);
        this.author = author;
    }
}

Movie class

public class Movie extends LibraryItem {
    private String director;

    protected Movie(int itemCode, String title, boolean isLent, int memberIdentifier, LocalDate dateLent, String director) {
        super(itemCode, title, isLent, memberIdentifier, dateLent);
        this.director = director;
    }
}

I was thinking maybe there is some kind of check I can do for each row implemented so the correct value will be given,

This was my attempt:

public class CollectionController implements Initializable {
    @FXML
    private TableView<LibraryItem> libraryItemsTable;
    @FXML
    private TableColumn<LibraryItem, String> itemCodeColumn;
    @FXML
    private TableColumn<LibraryItem, String>  availableColumn;
    @FXML
    private TableColumn<LibraryItem, String>  titleColumn;
    @FXML
    private TableColumn<LibraryItem, String>  authorDirectorColumn;
    private LibraryService libraryService = new LibraryService();

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        initializeTableView();
    }

    private void initializeTableView() {
        List<LibraryItem> libraryItems = libraryService.getLibraryItems();

        itemCodeColumn.setCellValueFactory(new PropertyValueFactory<>("itemCode"));
        availableColumn.setCellValueFactory(new PropertyValueFactory<>("availability"));
        titleColumn.setCellValueFactory(new PropertyValueFactory<>("title"));
        
        // implement here check for each new row
        if (checkIfBook(row))
            authorDirectorColumn.setCellValueFactory(new PropertyValueFactory<>("author"));
        else
            authorDirectorColumn.setCellValueFactory(new PropertyValueFactory<>("director"));
        //

        libraryItemsTable.getItems().addAll(libraryItems);
    }

Solution

  • If you follow the advice here and avoid the use of PropertyValueFactory, the solution becomes reasonably clear:

    titleColumn.setCellValueFactory(data -> 
        new SimpleStringProperty(data.getValue().getTitle()));
    
    authorDirectorColumn.setCellValueFactory(data -> {
        LibraryItem item = data.getValue();
        if (item instanceof Book book) {
            return new SimpleStringProperty(book.getAuthor());
        } else if (item instanceof Movie movie) {
            return new SimpleStringProperty(movie.getProducer());
        } else {
            return null ;
        }
    });
    

    Here's a complete example (I simplified the model classes for brevity, but retained enough to demonstrate the point):

    import javafx.application.Application;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.scene.Scene;
    import javafx.scene.control.TableColumn;
    import javafx.scene.control.TableView;
    import javafx.scene.layout.BorderPane;
    import javafx.stage.Stage;
    
    import java.io.IOException;
    
    public class HelloApplication extends Application {
        @Override
        public void start(Stage stage) throws IOException {
            TableView<LibraryItem> table = new TableView<>();
            TableColumn<LibraryItem, String> titleColumn = new TableColumn<>("Title");
            TableColumn<LibraryItem, String> authorProducerColumn = new TableColumn<>("Author/Producer");
            table.getColumns().add(titleColumn);
            table.getColumns().add(authorProducerColumn);
    
            titleColumn.setCellValueFactory(data -> new SimpleStringProperty(data.getValue().getTitle()));
    
            authorProducerColumn.setCellValueFactory(data -> {
                LibraryItem item = data.getValue();
                if (item instanceof Book book) {
                    return new SimpleStringProperty(book.getAuthor());
                } else if (item instanceof Movie movie) {
                    return new SimpleStringProperty(movie.getProducer());
                } else return null ;
            });
    
            for (int i = 1 ; i <= 10 ; i++) {
                Book book = new Book("Book "+i, "Author "+i);
                Movie movie = new Movie("Movie "+i, "Producer "+i);
                table.getItems().addAll(book, movie);
            }
    
            BorderPane root = new BorderPane(table);
            Scene scene = new Scene(root);
            stage.setScene(scene);
            stage.show();
        }
    
        public class LibraryItem {
            private String title ;
            public LibraryItem(String title) {
                this.title = title ;
            }
    
            public String getTitle() {
                return title;
            }
    
            public void setTitle(String title) {
                this.title = title;
            }
        }
    
        public class Movie extends LibraryItem {
            private String producer ;
            public Movie(String title, String producer) {
                super(title);
                this.producer = producer ;
            }
    
            public String getProducer() {
                return producer;
            }
    
            public void setProducer(String producer) {
                this.producer = producer;
            }
        }
    
        public class Book extends LibraryItem {
            private String author ;
            public Book(String title, String author) {
                super(title);
                this.author = author ;
            }
    
            public String getAuthor() {
                return author;
            }
    
            public void setAuthor(String author) {
                this.author = author;
            }
        }
    
        public static void main(String[] args) {
            launch();
        }
    }
    

    enter image description here