Question

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!

Was it helpful?

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);

OTHER TIPS

I struggled as well getting this to work properly in Yank. Here's what worked for me in Java 6:

public static <T> List<T> queryObjectListSQL(String poolName, String sql, Class<T> type, Object[] params) {

    List<T> returnList = null;

    Connection con = null;

    try {
      con = DB_CONNECTION_MANAGER.getConnection(poolName);

      if (con == null) {
        throw new ConnectionException(poolName);
      }

      con.setAutoCommit(false);

      BeanListHandler<T> resultSetHandler = new BeanListHandler<T>(type);

      returnList = new QueryRunner().query(con, sql, resultSetHandler, params);

      con.commit();

    } catch (Exception e) {
      logger.error(QUERY_EXCEPTION_MESSAGE, e);
      try {
        con.rollback();
      } catch (SQLException e2) {
        logger.error(ROLLBACK_EXCEPTION_MESSAGE, e2);
      }
    } finally {
      DB_CONNECTION_MANAGER.freeConnection(poolName, con);
    }

    return returnList;
  }

The full source code is here. The above code is obviously different from your exact example, but you can see how the generics works. The neat thing with this is that you don't have to cast at all when calling the method.

I hope that helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top