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

107,832

Solution 1

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

Solution 2

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

Solution 3

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

Solution 4

Depending on how you actually retrieve your data, you can do something like this:

private static <T> List<T> getAll(Class<T> cls){
  List<T> fromSql = (List<T>) sql.query("SELECT * FROM objects WHERE type="+cls.getName());
  return fromSql;
}

This requires your sql object to return the correct type of list, which O/R mappers like iBatis do.

If you need to differentiate between the passed types, you can still do a switch/case on cls.

Solution 5

Well, I really don't know if you need it this way. But here is a polymorphic approach. It might help somewhere somehow.

Create different objects for different tables all implementing a common interface. This means you represent each table as an object.

import java.util.LinkedList;

public class DataAccessTest 
{

    /**
     * @param args
     */
    public static void main(String[] args) 
    {
        DataAccess myDataAccessObject = new DataAccess();
        Type type1 = new Fruit();
        Type type2 = new User();
        LinkedList<Type> list1 = myDataAccessObject.getAllRecords(type1);
        LinkedList<Type> list2 = myDataAccessObject.getAllRecords(type2);
        LinkedList<Type> list3 = myDataAccessObject.getAllRecords(new Fruit());
        LinkedList<Type> list4 = myDataAccessObject.getAllRecords(new User());
    }
}

class DataAccess
{
    public LinkedList<Type> getAllRecords(Type type)
    {
        return type.getAllRecords();
    }
}

interface Type
{
    public LinkedList<Type> getAllRecords();
}

class Fruit implements Type
{
    public LinkedList<Type> getAllRecords()
    {
        LinkedList<Type> list = new LinkedList<Type>();
        list.add(new Fruit());
        return list;
    }
}

class User implements Type
{
    public LinkedList<Type> getAllRecords() 
    {
        LinkedList<Type> list = new LinkedList<Type>();
        list.add(new User());
        return list;
    }
}
Share:
107,832

Related videos on Youtube

Jonas
Author by

Jonas

Passionated Software Developer interested in Distributed Systems

Updated on March 08, 2020

Comments

  • Jonas
    Jonas about 4 years

    I am designing a simple Data Access Object for my Java application. I have a few classes (records) that represents a single row in tables like User and Fruit.

    I would like to have a single method for getting all records of a specific type.

    For the moment I have it like this:

    public List<User> getAllUsers() {
     ...
    }
    
    public List<Fruit> getAllFruits() {
     ...
    }
    
    ....
    

    But I would like to have a single polymorphic method like this (wrong):

    public List<T> getAllRecords(Class<T> type) {
        if(type instanceof User) {
            // Use JDBC and SQL SELECT * FROM user
        } else if(type instanceof Fruit) {
            // Use JDBC and SQL SELECT * FROM fruit
        }
        return collection;
    }
    

    Example for uses:

    List<Fruit> fruits = myDataAccessObject.getAllRecrods(Fruit.class);
    List<User> users = myDataAccessObject.getAllRecords(User.class);
    

    How can I do this in Java?

    • polygenelubricants
      polygenelubricants over 13 years
      Effective Java 2nd Edition, Item 52: Refer to objects by their interfaces; you should prefer List<Fruit> to LinkedList<Fruit>
    • Jonas
      Jonas over 13 years
      @polygene: Thanks, I have updated my code now and read Item 52 :)
  • f1sh
    f1sh over 13 years
    This IS pretty close though if you change the parameter's type to T instead of List<T>...
  • f1sh
    f1sh over 13 years
    +1: This lets you specify how to exactly load the data by it's type. Reminds me of the strategy pattern.
  • jjnguy
    jjnguy over 13 years
    @poly, I don't fully understand what you gain with the typereference class, and why my solution doesn't work.
  • polygenelubricants
    polygenelubricants over 13 years
    See also: gafter.blogspot.com/2007/05/… - regarding the unchecked cast actually being unsafe if you use TypeReference with a type parameter.
  • jjnguy
    jjnguy over 13 years
    @poly, haha. I'm just going to move on...i have work to do. :(
  • polygenelubricants
    polygenelubricants over 13 years
    @Justin: I figured out MY source of confusion: I answered the subject line of the question more than its body. That is, how can we pass a Class -- a type token -- and get a generic collection of that type? The answer of course is that since Class can't capture generic type (since it's nonreifiable), we must use something like super type token. However, diving into the body of the question itself, I now think that HTC+STT may be an overkill.
  • Jonas
    Jonas over 13 years
    Thanks, this is a nice solution. But I having problem to use instanceof on list to do a differenct action for LinkedList<Fruit> and LinkedList<User>.
  • Jonas
    Jonas over 13 years
    Thanks for the good answer and good references. But the solution was complex. Seems like a very weak part of the Java language. I think I skip to implement this, it was to complex.
  • Jonas
    Jonas over 13 years
    Thanks, but with this solution I will end up with having the data access object in many classes. Not really what I want.
  • Jonas
    Jonas over 13 years
    There was also a solution on Java tutorials, but it was also complex using Reflections: download-llnw.oracle.com/javase/tutorial/extra/generics/…
  • jjnguy
    jjnguy over 13 years
    @Jonas, you would have to use reflection to get the declared generic type...My solution probably isn't the best.
  • Jonas
    Jonas over 13 years
    @Justin I like your solution because it's simple, but poly's is probably more correct, but that solution is much more complex. I will have a look on how to use Reflections for this. Thanks.
  • Carl Smotricz
    Carl Smotricz over 13 years
    Awesome. There are languages where all this machinery isn't necessary, but making it possible in Java is admirable.
  • aNish
    aNish over 13 years
    @Jonas: Just out of curiosity, different tables will be having different columns right? And in case there is any change or addition of columns, then, only the particular object is to be modified in the above code. It is loosely coupled though there will be a class for each table. But again the properties of the table are represented in the class.
  • jjnguy
    jjnguy over 13 years
    @Jonas, I know BalusC answered a question regarding the exact thing you need right here on Stack Overflow.
  • jjnguy
    jjnguy over 13 years
    @Jonas, check out this question: stackoverflow.com/questions/1942644/…
  • Jonas
    Jonas over 13 years
    Yes, this is an alternative. But, what is the advantage with this code compared with the code I already have? see the first code listing in my question.
  • Emil
    Emil over 13 years
    There wouldn't be much of an advantage.The only advantage I can think of is that you can segregate all the record fetching code into a single class and make use of the same function call instead of having different function names for different objects.
  • Emil
    Emil over 13 years
  • Jonas
    Jonas over 13 years
    Great! I like your second solution, and the third was also nice! I have read that link, and I actually posted it as a comment to polygenelubricants's answer a few days ago, but I didn't realize this trick. Thanks!