Search code examples
javagenericsjavafx-2javabeansapache-commons-dbutils

Generic type fumbling on reusable JavaFX data retrieval Service with DbUtils BeanListHandler


I'm attempting to create a generic JavaFX Service that will use DbUtils' BeanListHandler to hand an ObservableList back to the app GUI thread. The intention is to reuse it to load many tables into many different lists of different bean classes.

The problem I'm having is dealing with the generics in the call() method for the Task.

ICINGBean is an abstract class that all the beans I'm dealing with inherit fromextend.

public class StaticDataFetcher extends Service<ObservableList<? extends ICINGBean>> {
    private Class<? extends ICINGBean> beanClass;

    @Override
    protected Task createTask() {
        DataGetter dget = new DataGetter();
        dget.setBeanClass(beanClass);
        return dget;
    }

    public Class<? extends ICINGBean> getBeanClass() { return beanClass; }

    public void setBeanClass(Class<? extends ICINGBean> beanClass) { this.beanClass = beanClass; }
}


class DataGetter extends Task<ObservableList<? extends ICINGBean>> {
    private Class<? extends ICINGBean> beanClass;

    @Override
    protected ObservableList<? extends ICINGBean> call() {
        ObservableList<? extends ICINGBean> staticList;
        staticList = FXCollections.observableArrayList();   
        ResultSetHandler<List<? extends ICINGBean>> handler;
        handler = new BeanListHandler<? extends ICINGBean>(beanClass);
        try {
            List<? extends ICINGBean> resultList;
            resultList = EntryPoint.getQRunner().query("SELECT * FROM ?", handler, beanClass.getSimpleName());
            staticList = FXCollections.observableList(resultList);
        } catch (SQLException ex) {
                Logger.getLogger(DataGetter.class.getName()).log(Level.SEVERE, null, ex);
        }
        return staticList;
    }

    public Class<? extends ICINGBean> getBeanClass() { return beanClass; }

    public void setBeanClass(Class<? extends ICINGBean> beanClass) { this.beanClass = beanClass; }
}

The compile-time error I'm getting is:

.../ICING/src/com/cccg/icing/StaticDataFetcher.java:55: error: unexpected type
            handler = new BeanListHandler<? extends ICINGBean>(beanClass);
                                         ^
  required: class or interface without bounds
  found:    ? extends ICINGBean

I'm pretty sure I'm just messing up the generics handling royally, but I'm not sure how. I've followed the listed example on the DbUtils example page for using a BeanListHandler, substituting where I thought appropriate to use a generic type with it, but I'm getting nowhere on the error.

Any help is greatly appreciated, thanks!

Solved!

With the helpful suggestion of Paul Bellora below I was able to solve this. I declared a type parameter for the class and used that along with the diamond operator.

public class StaticDataFetcher<T extends ICINGBean> extends Task<ObservableList<? extends ICINGBean>> {
    private Class<T> beanClass;
    //...

    public StaticDataFetcher(Class<T> beanClass) {
        super();
        this.beanClass = beanClass;
    }

    protected ObservableList<? extends ICINGBean> call() {
        //...
        ResultSetHandler<List<T>> handler;
        handler = new BeanListHandler<>(beanClass);
        //...
    }
}

Thanks for the help everybody, I hope this helps others!


Solution

  • You're not allowed to instantiate a generic type with wildcard type arguments. See my explanation about why in this answer.

    A simple solution is to use the diamond operator (Java 7 and later):

    handler = new BeanListHandler<>(beanClass);
    

    If that's not available, you need to use a generic helper method:

    <T extends ICINGBean> BeanListHandler<T> makeContainer(Class<T> beanClass) {
        return new BeanListHandler<T>(beanClass);
    }
    
    ...
    
    handler = makeContainer(beanClass);