Marcus MacWilliam
Marcus MacWilliam

Reputation: 612

How do I retrieve the Class for a Parameterized Class

Consider the following code:

public class Context {
    private final Class<?> clazz;
    private final String resource;
    private final com.thirdparty.Context context;

    public Context(final String resource, final Class<?> clazz) {
        this.clazz = clazz;
        this.resource = resource;
        this.context = com.thirdparty.Context.newInstance(this.clazz);
    }

    public String marshall(final Object element) {
        return this.context.marshall(element);
    }

    public Object unmarshall(final String element) {
        return this.context.unmarshall(element);
    }
}
Context context = new Context("request.xsd", Request.class);

// Marshall
Request request = new Request();
String xml = context.marshall(request);

// Unmarshall
Request roundTrip = Request.cast(context.unmarshall(xml));

I am trying to replace it with a generics version of the Context class:

public class Context<T> {
    private final Class<T> clazz;
    private final String resource;
    private final com.thirdparty.Context context;

    public Context(final String resource) {
        this.clazz = initHere(); // <== HOW ??
        this.resource = resource;
        this.context = com.thirdparty.Context.newInstance(this.clazz);
    }

    public String marshall(final T element) {
        return this.context.marshall(element);
    }

    public T unmarshall(final String element) {
        return this.clazz.cast(this.context.unmarshall(element));
    }
}
Context<Request> context = new Context<>("request.xsd");

// Marshall
Request request = new Request();
String xml = context.marshall(request);

// Unmarshall
Request roundTrip = context.unmarshall(xml);

Thus I do not pass a .class as a parameter to the constructor, and the unmarshall method automatically casts the return object.

I need to know the Class of T to pass to the newInstance() method, and to invoke the cast() method. i.e. T.class or T.getClass().

In my example I am attempting to initialise the clazz member, during the constructor, so that I can use it in both locations.

I have tried the following:

this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];

However getClass().getGenericSuperclass() returns an object which cannot be cast into a ParameterizedType. I cannot use any third party reflection libraries, I need to stick to the standard mechanisms inside the Jdk.

Upvotes: 5

Views: 551

Answers (3)

Valentin Ruano
Valentin Ruano

Reputation: 2809

Actually you are extremely close to the actual solution... your first version of the code is quite close to what you need:

From:

public Context(final String resource, final Class<?> clazz) {
        //...
}

To:

public Context(final String resource, final Class<T> clazz) {
       // ...
}

A single ? to T change would do the trick.

Upvotes: 1

Genu
Genu

Reputation: 827

You can't do that. In your case, the Generic Type T is linked to your instance. Reflection data are bound to Class, and your class doesn't define the type T.

The code you are trying to use works only if you define a class where T is set up.

public class RequestContext extends Context<Request> {}

If you use an instance of this class, then your code should work.

Upvotes: 4

SeverityOne
SeverityOne

Reputation: 2691

You can't do this in the constructor. If you don't pass a Class instance to the constructor, there is no way for the Context class what kind of object it is dealing with.

Have a look here: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html#Reflection

To determine the type and the generic type, you need an object. And you don't have an object.

Is there a specific reason why you can't add a Class parameter to the constructor? If there is, you example doesn't make it clear.

Upvotes: 3

Related Questions