Search code examples
javagenericsbounded-types

Bounded class as parameter


I'm driving crazy with the bounded class...

I defined such a method :

protected <X> DataAccessResult<List<X>> executeQuery(final String query, final Class<X> elementType,Object... args)

And I want to call it with the following arguments: Query : a SQL query, what it actually does doesn't matter elementType : I need something like Map<String,Object>.class, because what I need to get is a DataAccessResult<List<Map<String,Object>>>

But Map<String,Object>.class doesn't compile, and Map.class doesn't allow me to have a DataAccessResult<List<Map<String,Object>>> as result type.

I mean :

DataAccessResult<List<Map>> x = executeQuery("Select * from table",Map.class, (Object[]) null);
DataAccessResult<Map<String,Object>>> y = (DataAccessResult<Map<String,Object>>>) x;

provoke a cast error and DataAccessResult<List<Map<String,Object>>> x = executeQuery("Select * from table",Map.class, (Object[]) null); doesn't work either..

What could be the correct syntax?

Thanks!


Solution

  • Short answer: you can not really do it. At least not in normal Java syntax. There are workarounds however.

    The first is, you just explicitly specify X on the caller side this way:

    protected <X> DataAccessResult<List<X>> executeQuery(final String query, Object... args)
    
    this.<Map<String, Object>>executeQuery("Select * from table");
    

    You could also pass in an object instead of a class, that can "carry" the generic parameters:

    protected <X> DataAccessResult<List<X>> executeQuery(final String query, X prototype, Object... args)
    
    executeQuery("Select * from table", new Map<String, Object>());
    

    Both of these require no additional work, but are admittedly clumsy. The third option is to introduce a replacement for the Class<T> construct, which can carry generic parameters. This would word like this:

    protected <X> DataAccessResult<List<X>> executeQuery(final String query, JavaType<X> type, Object... args)
    
    executeQuery("Select * from table", map(String.class, Object.class));
    

    Where JavaType could look like this:

    public class JavaType<T> {
        public static <K, V> JavaType<Map<K, V>> map(Class<K> keyType, Class<V> valueType) {
            new JavaType<Map<K, V>>();
        }
    }
    

    This JavaType<T> class can be then extended with normal classes, lists, whatever you need. It could even carry the necessary class objects if you need them for reflection.