While the runtime class of the object determines the value of the type parameter, you can infer its actual value by recursively replacing the parameters of the formal type with the actual ones obtained from Class.getGenericSuperClass ():
class Substitution extends HashMap<String, TypeExpr> { Substitution(TypeVariable[] formals, TypeExpr[] actuals) { for (int i = 0; i < actuals.length; i++) { put(formals[i].getName(),actuals[i]); } } } abstract class TypeExpr { abstract TypeExpr apply(Substitution s); public abstract String toString(); static TypeExpr from(Type type) { if (type instanceof TypeVariable) { return new TypeVar((TypeVariable) type); } else if (type instanceof Class) { return new ClassType((Class) type); } else if (type instanceof ParameterizedType) { return new ClassType((ParameterizedType) type); } else if (type instanceof GenericArrayType) { return new ArrayType((GenericArrayType) type); } else if (type instanceof WildcardType) { return new WildcardTypeExpr((WildcardType) type); } throw new IllegalArgumentException(type.toString()); } static TypeExpr[] from(Type[] types) { TypeExpr[] t = new TypeExpr[types.length]; for (int i = 0; i < types.length; i++) { t[i] = from(types[i]); } return t; } static TypeExpr[] apply(TypeExpr[] types, Substitution s) { TypeExpr[] t = new TypeExpr[types.length]; for (int i = 0; i < types.length; i++) { t[i] = types[i].apply(s); } return t; } static void append(StringBuilder sb, String sep, Object[] os) { String s = ""; for (Object o : os) { sb.append(s); s = sep; sb.append(o); } } } class TypeVar extends TypeExpr { final String name; public TypeVar(String name) { this.name = name; } public TypeVar(TypeVariable var) { name = var.getName(); } @Override public String toString() { return name; } @Override TypeExpr apply(Substitution s) { TypeExpr e = s.get(name); return e == null ? this : e; } } class ClassType extends TypeExpr { final Class clazz; final TypeExpr[] arguments;
Test code:
public static void check(Class c, Class sc, String expected) { String actual = getSuperClassType(c, sc).toString(); if (!actual.equals(expected)) { throw new AssertionError(actual + " != " + expected); } } public static void main(String[] args) { check(Substitution.class, Map.class, "Map<String, TypeExpr>"); check(HashMap.class, Map.class, "Map<K, V>"); check(Bar.class, Foo.class, "Foo<List<? extends String[]>>"); } } interface Foo<X> { } class SuperBar<X, Y> implements Foo<List<? extends Y[]>> { } class Bar<X> extends SuperBar<X, String> { }
If, on the other hand, the class does not determine the value of the type parameter, you need to extend the bean to save the class object for the actual type parameter at runtime in other ways, for example. by doing:
class Super<T> { final Class<T> clazz; T foo; Super(Class<T> clazz) { this.clazz = clazz; } public T getFoo() { return foo; } public T setFoo() { this.foo = foo; } }
meriton
source share