9

I might blind some people here but I will fire anyway.

I know I can do:

Class<Response> c = Response.class;

To get the class of the object. Assuming that the Response object is Response<T> I want to do something like the following

Class<Class<User>> c =  Response<User>.class;

My full problem:

public class RequestHelper<T> extends AsyncTask<String, Response, T> {

    @Override
    protected T doInBackground(String... strings) {
       ...
       Response <T> r = objectMapper.readValue( result, Response.class );
       return r .getResponse();
    }
}

//and

public class Response <R> {
    private R response;
    public R getResponse() {
        return response;
    }
}

But above the parameter has not been specified in the assignment. In theory the correct way would require:

public class RequestHelper<T> extends AsyncTask<String, Response, T> {

    @Override
    protected T doInBackground(String... strings) {
       ...
       Response <T> r = objectMapper.readValue( result, Response <T>.class );
       return r.getResponse();
    }
}

But that generates a "Cannot select from parameterized type" lint error.

Alternatively I could pass the class in the constructor:

public class RequestHelper<T> extends AsyncTask<String, Response, T> {

    ....
    public RequestHelper(Class<Class<T>> tClass){
         this.tClass = tclass;
    }

    @Override
    protected T doInBackground(String... strings) {
       ...
       Response <T> r = objectMapper.readValue( result, tclass );
       return r.getResponse();
    }
}

// where the init of the class would have been:

 new RequestHelper<User>( Response<User>.class );
Diolor
  • 12,142
  • 23
  • 103
  • 165
  • 3
    [Type erasure](http://docs.oracle.com/javase/tutorial/java/generics/erasure.html) - There is no way to get information at run time about a generic type parameter, because it doesn't exist after compilation. Passing in a class explicitly is a common workaround. – khelwood Sep 26 '14 at 19:29
  • Jackson provides types tokens for this. – Sotirios Delimanolis Sep 26 '14 at 19:52
  • I think you can't capture generic parameters within a class, but you can capture generic parameters within a subclass. I think that might be what @SotiriosDelimanolis is referring to. – Jason Sep 26 '14 at 20:26

1 Answers1

6

As the comments suggest, there is no such thing as

Class<GenericType<GenericArgument>>

Or rather, it doesn't do what you might think it does. Each type declaration (class, interface, enum, primitive) gets a single Class instance, whether it is generic or not.

As such, even if you have a reference of type Class<GenericType<ArgumentOne>> and another of type Class<GenericType<ArgumentTwo>>, the instance they are referencing will be exactly the same. What's more, there will be absolutely no way to query the type argument type.

You seem to be using Jackson's ObjectMapper. It provides a kind of hack for getting generic type information in the form of type tokens. You can use a TypeReference or JavaType instance to represent generic types. Examples here and elsewhere around the Internet.

Community
  • 1
  • 1
Sotirios Delimanolis
  • 252,278
  • 54
  • 635
  • 683
  • Thanks Sotiri. Particularly for the above I used `new TypeReference>() {}` as class parameter which sets and provides the type. – Diolor Sep 28 '14 at 02:15
  • @Diolor Careful with `T` as the type argument, you're not providing any concrete type. Jackson will still use its defaults. This _hack_ only works with concrete types, not type variables. – Sotirios Delimanolis Sep 28 '14 at 02:32
  • 1
    Well...yes. The only way for the above `T` to work is to pass a constant type in the variable e.g. `@JsonDeserialize(as=User.class) private T response;` which is the same as `new TypeReference>() {}`. Dead end. – Diolor Sep 28 '14 at 02:45
  • Sir, can you throw some light on the same issue I am facing: https://stackoverflow.com/questions/45215475/android-generic-recyclerview-adapter-with-data-binding – Chintan Soni Jul 20 '17 at 13:12