52

I have an abstract class:

public abstract class RootProcessor<T> {
    Class<T> clazz;
}

I need to fill ClassT clazz; with the children of RootProcessor - every child has its own T

I found only one solution, but it needs compiler argument -Xlint:unchecked

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

Is this the best solution? Can we do the same without -Xlint:unchecked ?

Steve P.
  • 14,489
  • 8
  • 42
  • 72
Victor Mezrin
  • 2,797
  • 2
  • 32
  • 48
  • 5
    Don't use `-Xlint:unchecked`, use `@SuppressWarnings` as locally as possible if you need unsafe casts. – millimoose May 28 '13 at 18:42
  • 4
    And, yeah, there is no way to get `Class` safely at runtime if the compiler can't track the type of `T` from beginning to end. The typesafe but boilerplatey approach here is to simply introduce a protected constructor that takes `clazz` as a parameter. – millimoose May 28 '13 at 18:44
  • @millimoose: Why don't you write that as an answer? I have very little to add to your comments ;-) – meriton May 28 '13 at 18:46
  • yes, its a great answer. I think constructor with a parameter is a great solution. Its simple answer for this question ))) – Victor Mezrin May 28 '13 at 18:50
  • As an example: `key = gson.fromJson(json, key.getClass().getGenericSuperclass());` fixes `key = gson.fromJson(json,HashMap.class);` – User8461 Mar 10 '19 at 10:50

2 Answers2

39

The typesafe, but boilerplatey way to do this is to pass the Class<T> token "where the compiler can see it":

public abstract class RootProcessor<T> {
    Class<T> clazz;

    protected RootProcessor<T>(Class<T> clazz) {
        this.clazz = clazz;
    }
}

public class FooProcessor extends RootProcessor<Foo> {
    public FooProcessor() {
        super(Foo.class);
    }
}

If you're doing an unchecked cast but you "know what you're doing" and want the compiler to stop complaining, the correct approach would be localising the non-type-safe-but-you-know-they-work bits and using @SuppressWarnings:

public abstract class RootProcessor<T> {
    Class<T> clazz;
    { initClazz(); }

    @SuppressWarnings("unchecked")
    private void initClazz() {
        // the usual verbiage you already have in your question
        this.clazz = this.getClass().getGenericSuperclass().yadda().blah();
    }
}

(I won't hold this against you :P)

millimoose
  • 39,073
  • 9
  • 82
  • 134
3

There is a post of the same subject: Reflecting generics

And a class that implement it:TypeArgumentsUtils.java

An example is in the unit test.

So if you have this class:

public class BarProcessor extends RootProcessor<Bar> {
    public BarProcessor() {
    }
}

than you would get the first parameter with:

Class barClass = TypeArgumentsUtils.getFirstTypeArgument(
        RootProcessor.class, BarProcessor.class);
Alexander Suraphel
  • 10,103
  • 10
  • 55
  • 90