Nicolas Zozol
Nicolas Zozol

Reputation: 7048

How to specify a Generic Type as function parameter

I want to create a JPA transaction for a JSE app that returns a result.

@SuppressWarnings("unchecked")
public static <T> T transaction(EmWorker worker, Class<T>clazz){

    EntityManager em = createEntityManager();
    em.getTransaction().begin();

    //  >>> My Functional interface
    Object result = worker.work(em);

    em.getTransaction().commit();
    em.close();
    return (T)result;
}

It works for fetching a single object :

FunkoPop gandalf=EmFactory.transaction(   eManager -> { 
    return eManager.find(FunkoPop.class, 1);
}, FunkoPop.class);

But now, I want a List of FunkoPop.

List<FunkoPop> list =EmFactory.transaction(   e -> {

    String query = "SELECT f FROM FunkoPop f ";
    List<FunkoPop> l = e.createQuery(query, FunkoPop.class).getResultList();
    return l;

},  List<FunkoPop>.class);  //Won't compile ; or List.class gives warnings

The transaction needs two arguments : a lambda and a class. I approximatively understand that I cannot capture the type List<FunkoPop>.class as this parameter will loose the <FunkoPop> generic. What would be the solution to have no warnings ?

Upvotes: 1

Views: 237

Answers (3)

Nicolas Zozol
Nicolas Zozol

Reputation: 7048

Using Function<EntityManager, T> mentioned by @Holger is great. It's much simpler to implement : no Function Interface and no dirty cast. And it returns a checked type.

Implementation :

public static <T> T transaction(Function<EntityManager,T> worker){

    EntityManager em = createEntityManager();
    em.getTransaction().begin();

    T result = worker.apply(em);

    em.getTransaction().commit();
    em.close();

    return result;
}

Usage :

    FunkoPop gandalf = new FunkoPop("Gandalf");

    EmFactory.transaction( em -> {
        em.persist(gandalf);
        return gandalf;
    });

    List<FunkoPop> list =EmFactory.transaction( em -> {
        String query = "SELECT f FROM FunkoPop f ";
        return  em.createQuery(query, FunkoPop.class).getResultList();
    });

Upvotes: 0

mtj
mtj

Reputation: 3554

Add a second functional interface to produce lists:

public static interface EmListWorker<T> {
    public List<T> work(EntityManager em);
}

public static <T> List<T> transaction(EmListWorker<T> worker, Class<T>clazz){
    EntityManager em = createEntityManager();
    em.getTransaction().begin();

    //  >>> alternative Functional interface
    List<T> result = worker.work(em);

    em.getTransaction().commit();
    em.close();
    return result;
}

Upvotes: 1

NoDataFound
NoDataFound

Reputation: 11959

you can't because there is no List<FunkoPop> type. All there is a List.

You may however do it the hard way:

List<T> transaction(EmWorker worker, Class<T>clazz) {
  ...
  List<T> l = (List<T>)e.createQuery(query, clazz).getResultList();
  return l;
}

You might need to rename the method.

Upvotes: 1

Related Questions