-2

I have a generic class as below

public class X<T> {
    Integer x1;
    T x2;
}

Is it possible to get the type of X by providing the class of certain T? Some thing like below code.

public <T> Class<?> getType(Class<T> cls) {
    return X<cls>.class;
}

===================== news =======================

Thank you all for your answers. Let me explain more details of my question.

I just want to deserialize an Object of X. Let say T = Integer.

if use Gson, the code would be:

X x = new Gson().fromJson("{x1: 1, x2: 2}", new TypeToken<X<Integer>>(){}.getType());

but I don't want to write so long everytime. I am thinking if there is way can simplify the code to be as below:

X x = deserializeFunc("{x1: 1, x2: 2}", Integer.class);
Sean Lin
  • 371
  • 3
  • 8
  • : ( Actually my purpose is to deserialize an object of type X using gson. And I don't want to pass the how class type of X .. – Sean Lin Mar 22 '17 at 04:17
  • 4
    If your issue is deserializing a class with Gson, you'd have better luck asking *that* question, rather than how to mangle the type system. – dimo414 Mar 22 '17 at 04:20

3 Answers3

1

Generics are "erased" at runtime, which means (among other things) that there's only one Class object for a given generic class. For example, the below prints true:

final List<String> stringList = new ArrayList<String>();
final List<Integer> integerList = new ArrayList<Integer>();
System.out.println(stringList.getClass() == integerList.getClass());

So your question doesn't really make sense; your method has return-type Class<?>, so you obviously don't intend for the type argument to be available at compile-time; and X.class is just a single instance regardless of the type argument, so the type argument doesn't exist at runtime.

(But I'm not sure if that's a "yes" or a "no". I guess it depends on what you're trying to accomplish with this!)

ruakh
  • 175,680
  • 26
  • 273
  • 307
0

The general solution to this sort of problem is to require the caller pass in the Class into the constructor. There are many examples of this (e.g. EnumSet), and your X class might look like:

public class X<T> {
  private final Class<T> clazz;
  // fields

  public X(Class<T> clazz, /* arguments... */) {
    this.clazz = clazz;
    // set fields
  }

  public Class<T> getClass() {
    return clazz;
  }

  // ...
}

If your goal is to refer to the generic type (e.g. X<T> at runtime you'll need to use a different workaround. Gson offers exactly such a feature with TypeToken, which allows you to specify a generic type at runtime. This is discussed in the Gson User Guide, here's their example:

Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);

gson.fromJson(json, fooType);
dimo414
  • 47,227
  • 18
  • 148
  • 244
  • 1
    I *think* you've misunderstood the question. OP wants to get `X` from `T`, not vice-versa. (Not my downvote.) – shmosel Mar 22 '17 at 04:18
  • @shmosel I've expanded my answer to discuss Gson's `TypeToken`, which I believe is what OP is trying to get at. – dimo414 Mar 22 '17 at 04:24
  • So you're saying OP wants a type to represent a parameterized `X`. Not exactly what I understood, but you're probably right. Though he may want the parameter type to be dynamic, which I don't think is supported by Gson's `TypeToken` (as opposed to [Guava's](https://google.github.io/guava/releases/snapshot/api/docs/com/google/common/reflect/TypeToken.html#where-com.google.common.reflect.TypeParameter-java.lang.Class-)). – shmosel Mar 22 '17 at 04:30
0

It is not. The erasures that happen in java, don't allow for that to happen. I less than ideal solution is to create a field of type Class on your class that you can then later look at:

public class X<T> {
    final Class<T> myType;
    Integer x1;
    T x2;
    public X(Class<T> myType) {
      this.myType = myType;
    }
}

then you could do x.myType to get the class of the generic type.

pgreen2
  • 3,601
  • 3
  • 32
  • 59