从非泛型类中获取类的泛型类型

时间:2017-03-02 02:11:18

标签: java generics

我有一个这样的课程

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);
    }
}

当我运行(ParameterizedType)getClass().getGenericSuperclass()时, 期望是T,但它是PropertyEditorSupport,并且无法转换为ParameterizedType;

如果我删除extends PropertyEditorSupport,那么我可以获得通用类型。(事实证明这是错误的,我在这里犯了错误。)

似乎我们不能使用(ParameterizedType)getClass().getGenericSuperclass()从父类没有泛型类的类中获取泛型类型。

那么我怎样才能获得像RequestDataEditor这样的类的泛型类型?

not duplicate with the question,我想知道在使用(ParameterizedType)getClass().getGenericSuperclass()并且类具有非通用类型超类时的不同之处。

2 个答案:

答案 0 :(得分:2)

(ParameterizedType)getClass()。getGenericSuperclass()将获取当前类的超类,而不是通用类。您需要当前类的泛型类型而不是其超类。在您的示例中,这将是不可能的,因为此数据在运行时不再可用。可能的解决方案可能是:

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);
    }
}

我知道这并不是你想要达到的目标,但它是你能得到的最接近的(据我所知)。

答案 1 :(得分:2)

  

当我运行(ParameterizedType)getClass()。getGenericSuperclass()时,   期望是T,但它是PropertyEditorSupport,并且不能被转换为   ParameterizedType;

如果您想要访问T又称类型参数,则适当的反映方法是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]);
  }
}

如果您运行上面的代码,您将获得以下内容:

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

请注意,type参数未解析为Integer,而是解析为T,因为此信息在运行时不可用。

如果您对此信息的访问权限很重要,那么您有两个直接的选择。

一个。该对象包含对实际类型类的引用:

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中。您创建了一个子类,为该类型参数提供了具体的类:

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

请注意,gint4类不是Generic<Double>,而是由于构造函数后面的{}而扩展它的匿名内部类...这是完整class DoubleGeneric ...声明的替代方法。

在这两种替代方案中,A。和B.,A是优选的,因为通常应该避免依赖反射;除此之外,它更容易隐藏运行时错误,代码更难以维护。

  

我想知道使用时有什么不同   (ParameterizedType)getClass()。getGenericSuperclass()与一个类有   一个非通用类型超类。

如果你查看Type文档,这个方法返回的引用类,它表明它是Java语言中可用的所有对象/数据类型的“伞形”超级接口,包括甚至原语...所以对于没有通用超类的类,我猜它会返回一个反映那个类型的子类......

快速实验表明,实际上它会返回一个'Class'对象,因此它等同于getSuperclass()。

也许其他人可以对此提供更明智的观点,但我认为这可能有点误解......我认为getSuperType()或类似的东西会更合适。也许最初它只支持较旧的Java版本(1.5?)中的泛型,并最终扩展到涵盖其他数据类型,但这是我的一个疯狂猜测,而不考虑其历史。