52

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?

Jonas
  • 121,568
  • 97
  • 310
  • 388

7 Answers7

39

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
  • 376,812
  • 128
  • 561
  • 623
  • 1
    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 Aug 03 '10 at 15:32
  • 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 Aug 03 '10 at 21:34
  • 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 Aug 03 '10 at 21:40
  • 1
    Awesome. There are languages where all this machinery isn't necessary, but making it possible in Java is admirable. – Carl Smotricz Aug 03 '10 at 22:15
32

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 fruit
        }
        else if(inst instanceof User)
        {
            // Use JDBC and SQL SELECT * FROM user
        }
        return list;
    }
}
SwaPpeR
  • 68
  • 6
Emil
  • 13,577
  • 18
  • 69
  • 108
  • 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 Aug 05 '10 at 08:46
  • 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 Aug 05 '10 at 10:06
  • 1
    check this link: http://download-llnw.oracle.com/javase/tutorial/extra/generics/literals.html – Emil Aug 06 '10 at 12:46
  • 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 Aug 06 '10 at 14:09
15

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
  • 136,852
  • 53
  • 295
  • 323
  • This IS pretty close though if you change the parameter's type to `T` instead of `List`... – f1sh Aug 03 '10 at 15:12
  • @poly, I don't fully understand what you gain with the typereference class, and why my solution doesn't work. – jjnguy Aug 03 '10 at 15:30
  • @poly, haha. I'm just going to move on...i have work to do. `:(` – jjnguy Aug 03 '10 at 15:41
  • @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 Aug 03 '10 at 16:35
  • Thanks, this is a nice solution. But I having problem to use `instanceof` on `list` to do a differenct action for `LinkedList` and `LinkedList`. – Jonas Aug 03 '10 at 21:10
  • @Jonas, you would have to use reflection to get the declared generic type...My solution probably isn't the best. – jjnguy Aug 03 '10 at 21:41
  • @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 Aug 03 '10 at 21:55
  • @Jonas, I know BalusC answered a question regarding the exact thing you need right here on Stack Overflow. – jjnguy Aug 04 '10 at 01:28
  • 1
    @Jonas, check out this question: http://stackoverflow.com/questions/1942644/get-generic-type-of-java-util-list/1942680#1942680 – jjnguy Aug 04 '10 at 01:32
5

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
  • 11,489
  • 3
  • 25
  • 51
4

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,079
  • 6
  • 11
  • 1
    +1: This lets you specify how to exactly load the data by it's type. Reminds me of the strategy pattern. – f1sh Aug 03 '10 at 15:21
  • Thanks, but with this solution I will end up with having the data access object in many classes. Not really what I want. – Jonas Aug 03 '10 at 21:37
  • @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 Aug 04 '10 at 01:18
1

I believe what you are trying to do is possible with a bit of generics magic. I had to solve the same problem just now and this is what I did:

public class ListArrayUtils{
   @SuppressWarnings("unchecked") // It is checked. 
   public static <T,E> List<T> filterByType(List<E> aList, Class<T> aClass){
      List<T> ans = new ArrayList<>();
      for(E e: aList){
         if(aClass.isAssignableFrom(e.getClass())){
            ans.add((T)e);
         }
      }
      return ans;
   }       
}

And unit tests:

public class ListArrayUtilsTest{
   interface IfA{/*nothing*/}
   interface IfB{/*nothing*/}
   class A implements IfA{/*nothing*/}
   class B implements IfB{/*nothing*/}
   class C extends A implements IfB{/*nothing*/}

   @Test
   public void testFilterByType(){
      List<Object> data = new ArrayList<>();
      A a = new A();
      B b = new B();
      C c = new C();
      data.add(a);
      data.add(b);
      data.add(c);

      List<IfB> ans = ListArrayUtils.filterByType(data, IfB.class);

      assertEquals(2, ans.size());
      assertSame(b, ans.get(0));
      assertSame(c, ans.get(1));
   }
}
Emily L.
  • 5,673
  • 2
  • 40
  • 60
0

I've actually done this in a generic data access library. See Norm. Full source code on Github.

ccleve
  • 15,239
  • 27
  • 91
  • 157