反射:将声明类的类型参数应用于字段

时间:2019-06-01 07:56:00

标签: java reflection type-erasure reification

我具有以下类结构:

class A<T> {
   B<T> b;
}

class B<T> {
   T t;
}

class StringConsumer {
   A<String> a;
}

class LongConsumer {
   A<Long> a;
}

鉴于我有一个消费者类之一的实例,是否可以获取consumer.a.b的类型参数?我发现,如果将a声明为StringA a;,其中class StringA extends A<String>使用TypeTools

,则能够解决相同的问题

2 个答案:

答案 0 :(得分:1)

由于您的使用者类声明了没有任何类型变量的字段a,因此您所需的所有键入信息都将在运行时出现。

虽然可以直接使用反射来实现您所要的内容,但是它很快就会变得疯狂,因此我强烈建议您使用GenTyRefGeAnTyRef(我维护的增强型分叉)。 / p>

StringConsumer consumer = new StringConsumer(); //Or LongConsumer

Field a = consumer.getClass().getDeclaredField("a");
//typeOfA represents A<String, String>
ParameterizedType typeOfA = (ParameterizedType) GenericTypeReflector.getExactFieldType(a, consumer.getClass());

Type[] args =  typeOfA.getActualTypeArguments();
System.out.println(args[0]); //String
System.out.println(args[1]); //String

Field b = A.class.getDeclaredField("b");
//or if you need it dynamic ((Class)typeOfA.getRawType()).getDeclaredField("b")
//or GenericTypeReflector.erase(typeOfA).getDeclaredField("b")
//typeOfB represents B<String>
ParameterizedType typeOfB = (ParameterizedType) GenericTypeReflector.getExactFieldType(b, typeOfA);
System.out.println(typeOfB.getActualTypeArguments()[0]); //String again

因此,您在这里要做的是从StringConsumer开始,获取其字段atypeOfA,即A<String, String>)的类型,然后使用该信息来获取字段类型btypeOfB,即B<String>)。 通过这种方式,您可以根据需要进行进一步的操作...

答案 1 :(得分:0)

准确给出所发布的代码,在这种情况下可以确定type参数。以下示例中的方法进行了一些假设,并且不进行任何错误或健全性检查,而仅用于显示导致所需类型信息的“路径”:

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

class A<T>
{
    B<T> b;
}

class B<T>
{
    T t;
}

class StringConsumer
{
    A<String> a;
}

class LongConsumer
{
    A<Long> a;
}

public class GenericTypeReflectionTest
{
    public static void main(String[] args) throws Exception
    {
        StringConsumer s = new StringConsumer();
        LongConsumer t = new LongConsumer();

        System.out.println(determineConsumedType(s));
        System.out.println(determineConsumedType(t));
    }

    private static Class<?> determineConsumedType(Object object) throws Exception
    {
        Class<?> c = object.getClass();
        Field field = c.getDeclaredField("a");
        Type genericType = field.getGenericType();
        ParameterizedType parameterizedType = (ParameterizedType)genericType;
        Type[] typeArguments = parameterizedType.getActualTypeArguments();
        Type consumedType = typeArguments[0];
        return (Class<?>) consumedType;
    }

}

(附带说明:在先前的评论中,我说过由于type erasure而无法实现。但这不是这种情况:顶级类是 not < / em>用Consumer<T>T==String实例化的T==Long,实际的类型信息保留在类的 field 中,明确声明要使用StringLong进行参数化,这就是为什么可以获得参数化类型的实际类型实参的原因,它们不是TypeVariable对象,而是实际的{{1 }}对象)