user489041
user489041

Reputation: 28294

Proper use of Java Generics

I have the following function:

/**
 * Finds all entities of a certain type
 * @param <T> The type of the entity
 * @param entityType The class of the entity
 * @return A list of all the entities found, null if the entity is not in the database
 * or on error
 */
public <T> List<T> findAll(Class entityType) 
{
    javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
    cq.select(cq.from(entityType));
    return getEntityManager().createQuery(cq).getResultList();
}

You will note it is rather repetitive. Is there anyway that I can refactor this function so that it does not need to take a Class as a parameter. Is there anyway that I can use the generic type being passed in?

Upvotes: 0

Views: 147

Answers (6)

Falmarri
Falmarri

Reputation: 48567

You can more or less do what you're asking in Scala. (I'm not sure if you can use scala, but just for reference, here it is).

object Main extends App{
    def findAll[T : Manifest]() : Array[T] = {
       var a = new Array[T](0)
       println(a.getClass)
       a
    }

    var result = findAll[String]()
    println(result.getClass)

    var result2 = findAll[Array[Int]]()
    println(result2.getClass)
}

By using the implicit Manfiest, the scala compiler keeps a record of what generics get erased on compile.

Upvotes: 0

Kevin Welker
Kevin Welker

Reputation: 7937

The simple answer is "no". Due to something called type erasure, all parameterized types are treated as Object.class at runtime in the compiled bytecode. The generic types are only used at compile time to prevent you from using things wrong.

Upvotes: 1

Michael Ekstrand
Michael Ekstrand

Reputation: 29090

No, you cannot — directly. In a couple paragraphs I'll show you another way around the problem.

Java generics are implemented via type erasure, meaning that all type information is stripped away at runtime. When your method is invoked, it knows that it is supposed to return a List, but at run time there is nothing to tell it that it is supposed to return List<Foo>. The ArrayList constructor doesn't need access to the class object to do its job; your code, however, does.

The way around this is to just pass the class which, since this is a generic method (as opposed to a generic class), you can do. If you change the declaration of your method to be

public <T> List<T> findAll(Class<T> entityType)

Then you can call it with the class:

findAll(String.class)

and the compiler will automatically detect that it is supposed to return a List<String>. It reduces the redundancy, but by inferring the type argument from the class rather than the other way around. This is the standard way to solve this kind of problem — it shows up a lot in libraries like Guava.

Upvotes: 2

Attila
Attila

Reputation: 28762

Java uses Type erasure, which means that the generic type parameter is not available at runtime (used at compile time to ensure your code is correct wrt to types). This means that to use persistence you will need to explicitly pass the class so the runtime can figure out how to do the persistence (e.g. what class the persisted object belongs to)

Upvotes: 1

Affe
Affe

Reputation: 47954

Not really, no. Generics are a compile time feature. At run time a caller of your API can supply any instance of Class<T> for entityType so you need it available at runtime to provide to hibernate. The compiler has no ability to basically build a separate version of the method for every possible T, which is what it would have to do in order to omit the class parameter.

Also, Class is a raw type, you are already using generics improperly ;)

Upvotes: 1

Istvan Devai
Istvan Devai

Reputation: 4022

The standard practice to create a "Generic DAO" is to have an abstract class that is parametrizable and have subclasses with a specific parameter. This way, the method itself is already parametrized with the correct type.

Take a look at this for an example:

http://netbeans.org/projects/samples/sources/samples-source-code/content/samples/javaee/AffableBean/src/java/session/AbstractFacade.java

Upvotes: 1

Related Questions