Search code examples
javagenericsreflectionjavafxobservablelist

ObservableArrayList: How to get type of generic through reflection?


I have an ObservableList of generic type CHILDITEMS, where <CHILDITEMS extends PlanItem>. How would I know what type the ObservableList is during runtime?

    /*Get a reference to the child items of the currently viewed item.*/
    ObservableList<CHILDITEMS> childItems = (ObservableList<CHILDITEMS>) viewing.getChildItems();
    /*Set the child items label to the type of the child items.*/
    childItemsLabel.setText("Name of CHILDITEMS class");

I can't use getFields because CHILDITEMS isn't really a field. Using getType on the ObservableList.class only returns the generic type "E", rather than what it is at run time.

The CHILDITEM type could be a Goal, Objective, Strategy, or Task. I'd like to know which it is during runtime.


Solution

  • As @Romski says in the comments, this information isn't even retained at runtime.

    If you know your list is non-empty, and you only put items of the exact type in your list (i.e. your ObservableList<P> only contains items of runtime type P, not any items that are instances of subclasses of P), then you can of course do list.get(0).getClass(), but that is an unlikely scenario and not very robust.

    You could also consider creating a wrapper for the list, using a type token to retain the type. Something like:

    public class TypedObservableList<P extends PlanItem> {
        private final Class<P> type ;
        private final ObservableList<P> list ;
    
        public TypedObservableList(Class<P> type) {
            this.type = type ;
            this.list = FXCollections.observableArrayList();
        }
    
        public Class<P> getType() {
            return type ;
        }
    
        public ObservableList<P> getList() {
            return list ;
        }
    }
    

    Now you end up with lots of code looking like

    TableView<Goal> goalTable = new TableView<>();
    TypedObservableList<Goal> goals = new TypedObservableList<>(Goal.class);
    goalTable.setItems(goals.getList());
    

    but at least you can now do

    TypedObservableList<? extends PlanItem> typedList = viewing.getChildItems();
    childItemsLabel.setText(typedList.getType().getSimpleName());
    

    You haven't said what viewing is, but you can perhaps come up with a similar solution in which that class holds the type token, so you'd end up with

    childItemsLabel.setText(viewing.getChildType().getSimpleName());