IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Type 反射获取泛型真实类型 -> 正文阅读

[移动开发]Type 反射获取泛型真实类型

Generic 中文翻译--> 泛型

当我们对一个泛型类进行反射时,需要的到泛型中的真实数据类型,来完成如json反序列化的操作。此时需要通

Type 体系来完成。 Type 接口包含了 一个实现类(Class) 和四个实现接口,他们分别是:
实现类:
TypeVariable
? ? ?泛型类型变量。可以泛型上下限等信息;
ParameterizedType
? ? ? 具体的泛型类型,可以获得元数据中泛型签名类型 ( 泛型真实类型 )
GenericArrayType
? ? ? 当需要描述的类型是 泛型类的数组 时,比如 List[],Map[] ,此接口会作为 Type 的实现。
WildcardType
? ? ? 通配符泛型,获得上下限信息;
重点是理解这四种接口如何对应程序中的类型的:
这个就是一个定义了泛型的类,下面会通过Type对这个类进行解析,看看都能获取什么信息
public class TypeTest<T extends TestA, V> {

    //TypeVariable
    public T param;

    //ParameterizedType
    public TypeTest<TestA, String> typeTest;

    //WildcardType
    public TypeTest<? extends TestF,? super V> typeTestWildcardType;

    //GenericArrayType :List<String>[] ParameterizedTypeArray   V[] typeVariableTypeArray
    public void test(List<String>[] ParameterizedTypeArray, V[] typeVariableTypeArray, List<String> list, String[] strings, int[] ints) {

   }
}

1.TypeVariable

类型变量, 范型信息在编译时会被转换为一个特定的类型, 而TypeVariable就是用来反映在JVM编译该泛型的信息。这个编译该泛型前:也就是说想靠这个是得不到泛型具体是哪个类型的
它的声明是这样的:?

public interface TypeVariable<D extends GenericDeclaration> extends Type


也就是说它跟GenericDeclaration有一定的联系, 我的理解:

说到TypeVariable类,就不得不提及Java-Type体系中另一个比较重要的接口---GenericDeclaration;含义为:声明类型变量的所有实体的公共接口;也就是说该接口定义了哪些地方可以定义类型变量(泛型);

通过查看源码发现,GenericDeclaration下有三个子类,分别为Class、Method、Constructor;也就是说,我们定义泛型只能在一个类中这3个地方自定义泛型;

此时,我们不禁要问,我们不是经常在类中的属性声明泛型吗,怎么Field没有实现 GenericDeclaration接口呢?

其实,我们在Field中并没有声明泛型,而是在使用泛型而已!

TypeVariable是指以泛型的类型作为成员变量,例如 类泛型声明Test<T>,声明后在类中使用这个泛型作为成员变量 T param;

它有如下方法:

  1. Type[] getBounds(): 获取类型变量的上边界, 若未明确声明上边界则默认为Object
  2. D getGenericDeclaration(): 获取声明该类型变量实体
  3. String getName(): 获取在源码中定义时的名字

注意:

  • 类型变量在定义的时候只能使用extends进行(多)边界限定, 不能用super;
  • 为什么边界是一个数组? 因为类型变量可以通过&进行多个上边界限定,因此上边界有多个
public class TypeMainTest {

    public static void main(String[] args) {

        //TypeVariable 直接以泛型为类型的属性
        try {
            Field param = TypeTest.class.getField("param");
            if (param.getGenericType() instanceof TypeVariable) {
                TypeVariable typeVariable = (TypeVariable) param.getGenericType();
                System.out.println("name:" + typeVariable.getName());
                System.out.println("GenericDeclaration:" + typeVariable.getGenericDeclaration());
                for (Type type : typeVariable.getBounds()) {
                    System.out.println("Bounds:" + type);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:

name:T
GenericDeclaration:class com.example.fragmentadaptertest.type.TypeTest
Bounds:class com.example.fragmentadaptertest.testjava.TestA

2.ParameterizedType

具体的范型类型, 如Map<String, String>

我感觉这个比较好用,例如在反射的时候拿到了一个Map或者List的实例,就可以通过ParameterizedType获取一下里面存的具体是什么类型
有如下方法:

  1. Type getRawType(): 返回承载该泛型信息的对象, 如上面那个Map<String, String>承载范型信息的对象是Map
  2. Type[] getActualTypeArguments(): 返回实际泛型类型列表, 如上面那个Map<String, String>实际范型列表中有两个元素, 都是String
  3. Type getOwnerType(): 返回是谁的member.(上面那两个最常用)
  4. getOwnerType通过方法的名称,我们大概了解到,此方法是获取泛型的拥有者,那么拥有者是个什么意思?Returns a {@code?Type} object representing the type that this type? ? * is a member of.? For example, if this type is {@code?O.I},? ? * return a representation of {@code?O}.? (摘自JDK注释)通过注解,我们得知,“拥有者”表示的含义--内部类的“父类”,通过getOwnerType()方法可以获取到内部类的“拥有者”;例如: Map? 就是 Map.Entry<String,String>的拥有者;
public class TypeMainTest {

    public static void main(String[] args) {

        //ParameterizedType  参数化类型例如:List<String>这种
        try {
            Field variableTypeTest = TypeTest.class.getField("typeTest");
            if (variableTypeTest.getGenericType() instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) variableTypeTest.getGenericType();
                System.out.println("RawType:" + parameterizedType.getRawType());
                for (Type type : parameterizedType.getActualTypeArguments()) {
                    System.out.println("type:" + type);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
     }
}

执行结果:

RawType:class com.example.fragmentadaptertest.type.TypeTest
type:class com.example.fragmentadaptertest.testjava.TestA
type:class java.lang.String

3.GenericArrayType

范型数组,组成数组的元素中有范型则实现了该接口; 它的组成元素是ParameterizedTypeTypeVariable类型,它只有一个方法:

  1. Type getGenericComponentType(): 返回数组的组成对象, 即被JVM编译后实际的对象
public class TypeMainTest {

    public static void main(String[] args) {
        //GenericArrayType 泛型数组,数组的元素的中有泛型
        try {
            Method method = TypeTest.class.getMethods()[0];
            System.out.println(method);
            Type[] types = method.getGenericParameterTypes(); //获取泛型参数的type
            for (Type type : types) {
                if (type instanceof GenericArrayType) {
                    System.out.println("type:" + type);
                    System.out.println("GenericArrayType:" + ((GenericArrayType) type).getGenericComponentType());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:

public void com.example.fragmentadaptertest.type.TypeTest.test(java.util.List[],java.lang.Object[],java.util.List,java.lang.String[],int[])
type:java.util.List<java.lang.String>[]
GenericArrayType:java.util.List<java.lang.String>
type:V[]
GenericArrayType:V

看结果五个参数只打印了前两个,上面说到了它的组成元素是ParameterizedTypeTypeVariable类型,上面第三个参数List不是数组,第四第五个是数组没有泛型

4.WildcardType

该接口表示通配符泛型, 比如? extends Number?和?? super Integer?它有如下方法:

  1. Type[] getUpperBounds(): 获取范型变量的上界
  2. Type[] getLowerBounds(): 获取范型变量的下界

注意:

  • 现阶段通配符只接受一个上边界或下边界, 返回数组是为了以后的扩展, 实际上现在返回的数组的大小是1
public class TypeMainTest {

    public static void main(String[] args) {

        //WildcardType
        try {
            Field field = TypeTest.class.getField("typeTestWildcardType");
            ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
            Type[] types = parameterizedType.getActualTypeArguments();
            for (Type type : types) {
                System.out.println("type:" + type);
                WildcardType wildcardType = (WildcardType) type;
                for (Type mType : wildcardType.getLowerBounds()) {
                    System.out.println("mTypeLowerBounds:" + mType);
                }
                for (Type mType : wildcardType.getUpperBounds()) {
                    System.out.println("mTypeUpperBounds:" + mType);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
     }
}

执行结果:

type:? extends com.example.fragmentadaptertest.testjava.TestF
mTypeUpperBounds:class com.example.fragmentadaptertest.testjava.TestF
type:? super V
mTypeLowerBounds:V
mTypeUpperBounds:class java.lang.Object

再写几个边界的例子:

  • List<? extends Number>, 上界为class java.lang.Number, 属于Class类型
  • List<? extends List<T>>, 上界为java.util.List<T>, 属于ParameterizedType类型
  • List<? extends List<String>>, 上界为java.util.List<java.lang.String>, 属于ParameterizedType类型
  • List<? extends T>, 上界为T, 属于TypeVariable类型
  • List<? extends T[]>, 上界为T[], 属于GenericArrayType类型

下面搞一个gson反序列化的例子来看一下Type的使用:

public class TestD {

   public static class Response<T> {
        T data;
        int code;
        String message;

        @Override
        public String toString() {
            return "Response{" + "data=" + data + ", code=" + code + ", message='" + message + '\'' + '}';
        }

        public Response(T data, int code, String message) {
            this.data = data;
            this.code = code;
            this.message = message;
        }
    }

    public static class Data {
        String result;

        public Data(String result) {
            this.result = result;
        }

        @Override
        public String toString() {
            return "Data{" + "result=" + result + '}';
        }
    }

    public static void main(String[] args) {
        Response<Data> dataResponse = new Response<>(new Data("数据"), 1, "成功");
        Gson gson = new Gson();
        String json = gson.toJson(dataResponse);
        System.out.println(json);

        Type type = new TypeToken<Response<Data>>(){}.getType();
        System.out.println(type);
        Response<Data> response = gson.fromJson(json,type);
        System.out.println(response.data.result);
        
    }

}

执行结果:

{"data":{"result":"数据"},"code":1,"message":"成功"}
com.example.fragmentadaptertest.testjava.TestD$Response<com.example.fragmentadaptertest.testjava.TestD$Data>
数据

如果不用TypeToken:

    public static void main(String[] args) {
        Response<Data> dataResponse = new Response<>(new Data("数据"), 1, "成功");
        Gson gson = new Gson();
        String json = gson.toJson(dataResponse);
        System.out.println(json);

//        Type type = new TypeToken<Response<Data>>(){}.getType();
//        System.out.println(type);
        Response<Data> response = gson.fromJson(json,Response.class);
        System.out.println(response.data.result);
    }

发现测试结果报错了:?

因为没有解析泛型的具体类型所以,不知道泛型的具体类型是Data,也就不能这样调用了

{"data":{"result":"数据"},"code":1,"message":"成功"}
Exception in thread "main" java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.example.fragmentadaptertest.testjava.TestD$Data
	at com.example.fragmentadaptertest.testjava.TestD.main(TestD.java:49)

在进行GSON反序列化时,存在泛型时,可以借助 TypeToken 获取Type以完成泛型的反序列化。但是为什么 使用TypeToken 要new TypeToken<Response<Data>>(){}创建对应的实现类?

? ? ?因为只有在使用时,创建对应的实现类时,确定泛型类型,编译才能够将泛型 signature信息记录到 Class 元数据中。
看一下TypeToken的源码然后验证一下为什么要 创建对应的实现类:
  protected TypeToken() {
    this.type = getSuperclassTypeParameter(getClass());
    this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
    this.hashCode = type.hashCode();
  }

  static Type getSuperclassTypeParameter(Class<?> subclass) {
    Type superclass = subclass.getGenericSuperclass();
    if (superclass instanceof Class) {
      throw new RuntimeException("Missing type parameter.");
    }
    ParameterizedType parameterized = (ParameterizedType) superclass;
    return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
  }

我们仿照着写个例子:

public class TestC<T> {

    public void test1(){
        Class<?> aClass = getClass();
        Type type = aClass.getGenericSuperclass();
        if (type instanceof Class) {
            throw new RuntimeException("Missing type parameter.");
        }
        ParameterizedType parameterized = (ParameterizedType) type;
        for (Type type1 : parameterized.getActualTypeArguments()){
            System.out.println(type1);
        }
    }
}
public class TypeMainTest {

    public static void main(String[] args) {
        TestC<TestD.Response<TestD.Data>> testC = new TestC<>();
        testC.test1();
     }

}

测试结果:

发现不创建对应的实现类,aClass.getGenericSuperclass()还是Class类型,并没有解析成泛型对应的Type类型

Exception in thread "main" java.lang.RuntimeException: Missing type parameter.
	at com.example.fragmentadaptertest.testjava.TestC.test1(TestC.java:14)
	at com.example.fragmentadaptertest.type.TypeMainTest.main(TypeMainTest.java:25)

改成创建对应的实现类:

创建实现类的时候,必须在后面也加上这个泛型的类型,否则编译不通过,估计就是因为上面说的:?因为只有在使用时,创建对应的实现类时,确定泛型类型,编译才能够将泛型 signature信息记录到Class元数据中。

public class TypeMainTest {

    public static void main(String[] args) {


        TestC<TestD.Response<TestD.Data>> testC = new TestC<TestD.Response<TestD.Data>>(){};
        testC.test1();
    }
}

测试结果:

com.example.fragmentadaptertest.testjava.TestD$Response<com.example.fragmentadaptertest.testjava.TestD$Data>

得到了泛型的真实类型,可见TypeToken<T>就是一个工具类,它的作用就是用来解析它泛型T的类型的,我们自己也可以实现。

Type及其子接口的来历

泛型出现之前的类型

没有泛型的时候,只有原始类型。此时,所有的原始类型都通过字节码文件类Class类进行抽象。Class类的一个具体对象就代表一个指定的原始类型。

泛型出现之后的类型

泛型出现之后,扩充了数据类型。从只有原始类型扩充了参数化类型、类型变量类型、限定符类型 、泛型数组类型。

与泛型有关的类型不能和原始类型统一到Class的原因

  • 产生泛型擦除的原因

原始类型和新产生的类型都应该统一成各自的字节码文件类型对象。但是由于泛型不是最初Java中的成分。如果真的加入了泛型,涉及到JVM指令集的修改,这是非常致命的。

  • Java中如何引入泛型

为了使用泛型又不真正引入泛型,Java采用泛型擦除机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部擦除。

  • Class不能表达与泛型有关的类型

因此,与泛型有关的参数化类型、类型变量类型、限定符类型 、泛型数组类型这些类型编译后全部被打回原形,在字节码文件中全部都是泛型被擦除后的原始类型,并不存在和自身类型对应的字节码文件。所以和泛型相关的新扩充进来的类型不能被统一到Class类中。

  • 与泛型有关的类型在Java中的表示

为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType, TypeVariable<D>, GenericArrayType, WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。

  • 引入Type的原因

为了程序的扩展性,最终引入了Type接口作为Class和ParameterizedType, TypeVariable<D>, GenericArrayType, WildcardType这几种类型的总的父接口。这样可以用Type类型的参数来接受以上五种子类的实参或者返回值类型就是Type类型的参数。统一了与泛型有关的类型和原始类型Class

  • Type接口中没有方法的原因

从上面看到,Type的出现仅仅起到了通过多态来达到程序扩展性提高的作用,没有其他的作用。因此Type接口的源码中没有任何方法。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-12-01 17:49:28  更:2021-12-01 17:51:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:55:43-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码