views:

363

answers:

5

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?

+4  A: 

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>());
jjnguy
This IS pretty close though if you change the parameter's type to `T` instead of `List<T>`...
f1sh
@poly, I don't fully understand what you gain with the typereference class, and why my solution doesn't work.
jjnguy
@poly, haha. I'm just going to move on...i have work to do. `:(`
jjnguy
@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.
polygenelubricants
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, you would have to use reflection to get the declared generic type...My solution probably isn't the best.
jjnguy
@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.
Jonas
@Jonas, I know BalusC answered a question regarding the exact thing you need right here on Stack Overflow.
jjnguy
@Jonas, check out this question: http://stackoverflow.com/questions/1942644/get-generic-type-of-java-util-list/1942680#1942680
jjnguy
+10  A: 

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

polygenelubricants
+1 For the extensive answer.
f1sh
See also: http://gafter.blogspot.com/2007/05/limitation-of-super-type-tokens.html - regarding the unchecked cast actually being unsafe if you use `TypeReference` with a type parameter.
polygenelubricants
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
There was also a solution on **Java tutorials**, but it was also complex using Reflections: http://download-llnw.oracle.com/javase/tutorial/extra/generics/literals.html
Jonas
Awesome. There are languages where all this machinery isn't necessary, but making it possible in Java is admirable.
Carl Smotricz
+2  A: 

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;
    }
}
aNish
+1: This lets you specify how to exactly load the data by it's type. Reminds me of the strategy pattern.
f1sh
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: 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.
aNish
+1  A: 

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.

f1sh
+2  A: 

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;
    }
}
Emil
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.
Jonas
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
check this link:http://download-llnw.oracle.com/javase/tutorial/extra/generics/literals.html
Emil
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!
Jonas