2

I have a class like this

public class RequestDataEditor<T> extends PropertyEditorSupport {
    @Override
    public void setAsText(String text) {

        MyData<T> value;

        try {
            //(ParameterizedType)getClass().getGenericSuperclass()

            value = JacksonUtil.parse(text, new TypeReference<MyData<T>>() {
            });
        } catch (Exception e) {
            value = null;
        }

        setValue(value);
    }
}

When I run (ParameterizedType)getClass().getGenericSuperclass(), expect to be T, but it's PropertyEditorSupport, and can't be casted to ParameterizedType;

If i remove the extends PropertyEditorSupport, then i can get the generic type.(Proved to be wrong, i made a mistake here.)

It seems that, we can't use (ParameterizedType)getClass().getGenericSuperclass() to get generic type from a class whose parent has no generic type.

So how can I get the generic type with such a class like RequestDataEditor?

not duplicate with the question, I want to know what's the different when use (ParameterizedType)getClass().getGenericSuperclass() with a class has a none generic type super class.

Community
  • 1
  • 1
JackYe
  • 122
  • 1
  • 2
  • 13
  • Generics are a compile time safety check. They don't contain any usable type information at run time. – 4castle Mar 02 '17 at 02:14
  • @4castle But we can get the generic type if the class `RequestDataEditor ` has no parent class – JackYe Mar 02 '17 at 02:17
  • If you remove `extends PropertyEditorSupport`, `getGenericSuperclass()` just returns `class Object`. Please provide a [mcve] to test. – kennytm Mar 22 '17 at 10:15
  • Sorry, I think i made an mistake on this. @kennytm – JackYe Mar 24 '17 at 02:41

2 Answers2

3

When I run (ParameterizedType)getClass().getGenericSuperclass(), expect to be T, but it's PropertyEditorSupport, and can't be casted to ParameterizedType;

If you want to have access to the T aka the type-parameter, the appropriate reflection method is Class.getTypeParameters.

// Test.java:
class Generic<T> {
}

class Main {

  public static void main(String[] args) {

     final Generic<Integer> gint1 = new Generic<Integer>();

     System.err.println("gint1's type-parameter: " 
                       + gint1.getClass().getTypeParameters()[0]);
  }
}

If you run the code above you get the following:

$ javac Test.java
$ java Main
gint1's type-parameter: T

Notice that the type parameter is not resolved as Integer but as T since this information in not available at run-time.

If access to this information is important for you, then you have two straight forward options.

A. the object contains a reference to the actual type class:

class Generic<T> {

    public final Class<T> type;

    public Generic(Class<T> type) {
        this.type = type;
    }

    public Class<T> type() {
        return type;
    }
}

class Main {
    public static void main(String[] args) {

        final Generic<Integer> gint2 = new Generic<>(Integer.class);
        System.err.println("gint2's type-parameter: " + gint2.type().getSimpleName());
    }
}
// Output will be "gint2's type-parameter: Integer".

B. You create a subclass that gives a concrete class for that type-parameter:

import java.lang.reflect.*;

class Generic<T> {
}

class IntegerGeneric extends Generic<Integer> {
}

class Main {
    public static void main(String[] args) {

        final Generic<Integer> gint3 = new IntegerGeneric();
        final Generic<Double> gint4 = new Generic<Double>() {};
        System.err.println("gint3's type-parameter: "
            + ((ParameterizedType)gint3.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
        System.err.println("gint4's type-parameter: "
            + ((ParameterizedType)gint4.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
    }
}
// Output:
// gint3's type-parameter: class java.lang.Integer
// gint4's type-parameter: class java.lang.Double

Notice that gint4 class is not Generic<Double> but at anonymous inner class that extends it due to the {} following the constructor... this is an alternative to a full class DoubleGeneric ... declaration.

Of these two alternatives, A. and B., A. is preferable as in general one should avoid to rely on reflection; amongst other things it is more prone to hide run-time errors and the code is more difficult to maintain.

I want to know what's the different when use (ParameterizedType)getClass().getGenericSuperclass() with a class has a none generic type super class.

If you look-up the Type documentation, the class of the reference returned by this methods, it indicates that it is an "umbrella" super-interface for all object/data types available in the Java language including even primitives ... so for a class that does not have a generic superclass I guess it would return a subclass of type that reflect that...

A quick experiment reveals that in fact it would return a 'Class' object so it would be equivalent to to getSuperclass().

Perhaps others can give a more informed perspective on this, but I think this might have been a bit of a misnaming ... I think getSuperType() or something like that would have been more appropriate. Perhaps initially it only supported generics in an older Java version (1.5?) and eventually was extended to cover other data types but this is a wild guess of mine without looking into its history.

Valentin Ruano
  • 2,726
  • 19
  • 29
2

The (ParameterizedType)getClass().getGenericSuperclass() will get the super class of your current class which is not a generic one. You want the generic type of the current class and not its super class. In your example this will be impossible because this data is no longer available at run time. A possible solution could be:

public class RequestDataEditor<T> extends PropertyEditorSupport {

    private final Class<T> genericType;

    public RequestDataEditor(Class<T> genericType) {
        this.genericType = genericType;
    }

    @Override
    public void setAsText(String text,  ) {

        MyData<T> value;

        try {
            //use genericType here

            value = JacksonUtil.parse(text, new TypeReference<MyData<T>>() {
            });
        } catch (Exception e) {
            value = null;
        }

        setValue(value);
    }
}

I know this isn't exactly what you try to achieve but it's the closest you can get (to my knowledge).

mahieus
  • 580
  • 3
  • 17
  • Thx. I made a mistake. Your answer help me to get a deeper understanding of the `getGenericSuperclass()` method. – JackYe Mar 23 '17 at 02:13