Search code examples
javainheritancejavafxcomboboxsuperclass

JavaFX custom ComboBox with generic class


I have two types of classes which extend class X

public class A extends X

and

public class B extends X

Within my JavaFX application, there needs to be a combobox. This comboBox is constructed by using either classes A or B, and uses the respective toString() method of that class to populate. To cut down on code duplication.

I'm trying to amend my current method for generating the comboBox, such that it will allow an ObservableList of type A or B.

protected ComboBox<? extends X> getComboBox(ObservableList<? extends X> list){
    ComboBox<? extends X> comboBox = new ComboBox<>();
    comboBox.setItems(list);
    comboBox.setCellFactory(new Callback<ListView<? extends X>, ListCell<? extends X>>() {
        @Override
        public ListCell<Controller> call(ListView<Controller> param) {

            return new ListCell<? extends X>(){
                @Override
                public void updateItem(Class<? extends X> content, boolean empty){
                    super.updateItem(content, empty);
                    if(!empty) {
                        setText(content.toString());
                    } else {
                        setText(null);
                    }
                }
            };
        }
    });
    return comboBox;
}

Since both the classes have a superclass, is it possible to achieve something like this (And potentially, but not essentially, return it as it's original class type)

@SillyFly - I retrieve the list using the Reflections dependency. It returns all classes of a specific type (i.e. A)

public ObservableList<A> getAList() {
    Reflections reflection = new Reflections("com.classes.aClasses");
    ObservableList<A> classesList = FXCollections.observableArrayList();
    for (Class<? extends A> thisClass : reflection.getSubTypesOf(A.class)) {
        try {
            classesList.add(thisClass.newInstance());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return classesList;
}

Solution

  • I'm pretty sure the default ListCell implementation uses toString to render items, so setting a cell factory may be redundant.

    As for your question - the problem is ComboBox<T> needs its items property to be ObservableList<T>. Since you are using generic wildcard as the type parameter, the compiler has no way of knowing whether or not this is the same type.

    What you need to do is give the method a generic type parameter:

    protected <T extends X> ComboBox<T> getComboBox(ObservableList<T> list){
        ComboBox<T> comboBox = new ComboBox<>();
        comboBox.setItems(list);
        // Setting a cell factory is probably redundant if you're only using toString...
        return comboBox;
    } 
    

    Note that we define a type parameter T and force it to be a type that extends X. All generic variables henceforth use this type parameter, so the compiler can know it all matches.