1

I have a generic class with a method that calls another method that requires a class object as one of its arguments. I would like to be able to use the class type that's set as the generic at object creation time but I can't quite seem to get the syntax right. Here's what I would like to do:

public abstract class GenericClass<T> {

    private void doStuff() {
        //do a bunch of stuff

        //This method's signature is methodToCall(Serializable s) and is not a method I can change
        methodToCall(T);
        methodToCall(Class<T>);
        methodToCall(T.class);

        //do some more stuff
    }

}

public class NonGenericClass extends GenericClass<DesiredClass> {

}

The Class object type is serializable so this works:

methodToCall(DesiredClass.class);

But I can't easily override the method because I'd just end up copying all the code since the class type is needed in the middle of the method.

Here's the best I've come up with so far:

public abstract class GenericClass<T> {

    protected abstract void doStuff();

    protected <T> void doStuff(Class<T> cls) {
        methodToCall(cls);
    }

}

public class NonGenericClass extends GenericClass<DesiredClass> {

    @Override
    protected void doStuff() {
        doStuff(DesiredClass.class);
    }

}

That works, but it feels like the generic class is wasting the knowledge that it has of what T is. Is there a syntax error in the 3 versions of calling methodToCall that I listed first? Is this something that just can't be done?

Hope this all makes sense.

1
  • 1
    Only the compiler knows the type and it is not available at runtime due to type erasure. Commented Oct 11, 2012 at 15:09

3 Answers 3

2

GenericClass needs a run time type token: a Class<T> object passed into its constructor and saved in a field. The code is approximately as verbose as your best attempt, but more standard:

class GenericClass<T> {

    Class<T> clazz;

    public GenericClass(Class<T> c) {
        clazz = c;
    }

    protected void doStuff() {
        methodToCall(clazz);
    }

}
Sign up to request clarification or add additional context in comments.

1 Comment

Ok, I failed to realize that Class<T> was a runtime type token. I was struggling on the thinking that the compiler could infer the T type and create the Class<T> object for me. Part of me still doesn't want to believe that it can't be done, but considering I can't get it to compile otherwise, I think I'll just have to deal with it. At least your solution is a little cleaner.
2

If you are calling a method whose parameter implements any type, then you must ensure you generic type, T implements/extends that type.

public class GenericClass<T extends Serializable>
{
    // ...
}

Comments

0

In this case, if you know that the actual runtime class of the object is always going to implement GenericClass with an actual class as the type argument, then you can retrieve that type argument from the metadata of the class (here we assume that the actual class is a direct subclass of GenericClass, to simplify the search):

private void doStuff() {
    Class<T> clazz = (Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    methodToCall(clazz);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.