How can I pass a Class as parameter and return a generic collection in Java? How can I pass a Class as parameter and return a generic collection in Java? java java

How can I pass a Class as parameter and return a generic collection in Java?


It looks like you want to adapt what Josh Bloch calls a Typesafe Heterogenous Container pattern: you are passing a type token Class<T>, and you want back a List<T>.

Plain old THC can map a Class<T> to a T in a typesafe manner, but since you actually want a List<T> instead, then you want to use what Neal Gafter calls the super type tokens.

The following snippet is adapted from Crazy Bob Lee's code posted in Neal Gafter's blog:

public abstract class TypeReference<T> {    private final Type type;    protected TypeReference() {        Type superclass = getClass().getGenericSuperclass();        if (superclass instanceof Class<?>) {            throw new RuntimeException("Missing type parameter.");        }        this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];    }    public Type getType() {        return this.type;    }}

Now you can create a super type token like these:

    TypeReference<String> stringTypeRef =            new TypeReference<String>(){};    TypeReference<Integer> integerTypeRef =            new TypeReference<Integer>(){};    TypeReference<List<Boolean>> listBoolTypeRef =            new TypeReference<List<Boolean>>(){};

Essentially you pass a TypeReference<T> instead of a Class<T>. The difference is that there is no List<String>.class, but you can make a TypeReference<List<String>>.

So now we can make our container as follows (the following is adapted from Josh Bloch's original code):

public class Favorites {    private Map<Type, Object> favorites =        new HashMap<Type, Object>();    public <T> void setFavorite(TypeReference<T> ref, T thing) {        favorites.put(ref.getType(), thing);    }    public <T> T getFavorite(TypeReference<T> ref) {        @SuppressWarnings("unchecked")        T ret = (T) favorites.get(ref.getType());        return ret;    }}

Now we can put the two together:

    Favorites f = new Favorites();    f.setFavorite(stringTypeRef, "Java");    f.setFavorite(integerTypeRef, 42);    f.setFavorite(listBoolTypeRef, Arrays.asList(true, true));    String s = f.getFavorite(stringTypeRef);    int i = f.getFavorite(integerTypeRef);    List<Boolean> list = f.getFavorite(listBoolTypeRef);    System.out.println(s);    // "Java"    System.out.println(i);    // "42"    System.out.println(list); // "[true, true]"

Neal Gafter argued in his blog that with some more bells and whistles, TypeReference for super type tokens will make a worthy inclusion in the JDK.

Attachments

References


Since you say that you don't want you data access methods in different classes(in the comment to anish's answer),I thought why not try something like this.

public class Records {    public interface RecordFetcher<T>{        public List<T> getRecords();    }    static RecordFetcher<Fruit> Fruit=new RecordFetcher<Fruit>(){        public List<Fruit> getRecords() {            ...        }    };    static RecordFetcher<User> User=new RecordFetcher<User>(){        public List<User> getRecords() {            ...        }       };    public static void main(String[] args) {        List<Fruit> fruitRecords=Records.Fruit.getRecords();        List<User> userRecords=Records.User.getRecords();    }}

EDIT:

I would like to add one more of my implementation.

public class Test {     public static void main(String[] args)     {        Test dataAccess=new Test();       List<Fruit> FruitList=dataAccess.getAllRecords(Fruit.myType);       List<User> UserList=dataAccess.getAllRecords(User.myType);    }     <T> List<T> getAllRecords(T cl)    {        List<T> list=new ArrayList<T>();        if(cl instanceof Fruit)        {             // Use JDBC and SQL SELECT * FROM fruit        }        else if(cl instanceof User)        {            // Use JDBC and SQL SELECT * FROM user        }        return list;    }}class Fruit{    static final Fruit myType;    static {myType=new Fruit();}}class User{    static final User myType;    static {myType=new User();}}

EDIT:

I think this implementation is just as you have asked

public class Test {     public static void main(String[] args) throws InstantiationException, IllegalAccessException     {        Test dataAccess=new Test();       List<Fruit> FruitList=dataAccess.getAllRecords(Fruit.class);       List<User> UserList=dataAccess.getAllRecords(User.class);    }     <T> List<T> getAllRecords(Class<T> cl) throws InstantiationException, IllegalAccessException    {        T inst=cl.newInstance();        List<T> list=new ArrayList<T>();        if(inst instanceof Fruit)        {             // Use JDBC and SQL SELECT * FROM user        }        else if(inst instanceof User)        {            // Use JDBC and SQL SELECT * FROM fruit        }        return list;    }}


You are pretty close.

public <T> LinkedList<T> getAllRecords(List<T> list) { ...}

This is called a Generic Method.

You will want to specify a parameter like List<T>. Then, based upon the type of the list you pass in, Java will infer the generic type to return.

Edit:

Poly's answer is very good. It should be easy enough for you to do the following and not have to create a TypeReference class.

List<Fruit> fruit = myDataAccessObject.getAllRecrods(new LinkedList<Fruit>());List<User> users = myDataAccessObject.getAllRecords(new LinkedList<User>());