java泛型和类型擦除

刺骨的言语ヽ痛彻心扉 2022-12-03 12:51 335阅读 0赞

java泛型和类型擦除
泛型的本质是参数化类型,这种参数类型可以用在类、接口和方法的创建中。泛型是在JAVA 1.5版本中才引入的,它能和以前的版本兼容的原因是泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,即类型擦除。
根据使用情况可以分为以下三种:
泛型类
泛型方法
泛型接口
下面是一个常用的泛型类:

  1. // 一个泛型类,可以根据需要包装不同结果的返回值
  2. public class Result<T> {
  3. private boolean success;
  4. private String message;
  5. private T data;
  6. // 一个泛型方法
  7. // 返回值类型定义前的<T>是必须的,用来声明一个类型持有者名称,然后就可以把T当作一个类型代表来声明成员、参数和返回值类型。
  8. public static <T> Result<T> success(T data) {
  9. Result<T> r = new Result<>();
  10. r.success = true;
  11. r.data = data;
  12. return r;
  13. }
  14. public static <T> Result<T> error(String message) {
  15. Result<T> r = new Result<>();
  16. r.message = message;
  17. return r;
  18. }
  19. // getter & setter
  20. }

为什么要用T而不是其它字母?事实上是可以任意字符串(如Result< something >),但是为了显得专业,一般约定几个大写字母在不同场景使用。

T 最常用,一般代指任意类,不知道用啥就用它
E 代表Element,一般用在集合的泛型场景
K 代表Key,一般和Value一起出现在键值对场景(如Entry)
V 代表Value,一般和Key一起出现在键值对场景(如Entry)
还有些不太常见的如S,U…
类型擦除

在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。

  1. import java.lang.reflect.Field;
  2. import java.util.Date;
  3. public class Main {
  4. public static void main(String[] args) throws NoSuchFieldException {
  5. Result<Date> r1 = Result.success(new Date());
  6. Result<Number> r2 = Result.success(2.333);
  7. dataType(r1);
  8. dataType(r2);
  9. }
  10. private static void dataType(Result<?> result) throws NoSuchFieldException {
  11. Field field = result.getClass().getDeclaredField("data");
  12. System.out.println(field.getType().toString());
  13. }
  14. }
  15. /* 输出: ​ class java.lang.Object class java.lang.Object ​ */

通过反射我们在运行时得到了data的类型,发现都是Object,证明代码编译后所谓泛型都没了,这就是泛型擦除。
通过反射绕过泛型限制

  1. import java.lang.reflect.Method;
  2. import java.util.ArrayList;
  3. import java.util.Date;
  4. public class Main {
  5. public static void main(String[] args) throws Exception {
  6. ArrayList<Integer> list = new ArrayList<Integer>();
  7. //正规途径
  8. list.add(1);
  9. //反射大法
  10. Method m = list.getClass().getMethod("add", Object.class);
  11. m.invoke(list, 2);
  12. m.invoke(list, 3.21);
  13. m.invoke(list, "对不起,我是字符串");
  14. m.invoke(list, new Date());
  15. for (Object x : list) {
  16. System.out.println(x.getClass().getName() + ":\t" + x);
  17. }
  18. }
  19. }
  20. /* 输出: ​ java.lang.Integer: 1 java.lang.Integer: 2 java.lang.Double: 3.21 java.lang.String: 对不起,我是字符串 java.util.Date: Sun Jul 28 23:49:34 CST 2019 ​ */

发表评论

表情:
评论列表 (有 0 条评论,335人围观)

还没有评论,来说两句吧...

相关阅读

    相关 java类型

    类型擦除: Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除。Java的泛型基本上都是在编译器这个层次

    相关 Java-类型

    Java泛型-类型擦除 一、概述 Java泛型在使用过程有诸多的问题,如不存在List.class, List不能赋值给List(不可协变),奇怪的ClassCastE

    相关 Java类型

    类型擦除详解: [来自CSDN的一篇我认为最好的讲解类型擦除的文章][CSDN]. 个人补充: 泛型擦除是指Java 编译后的字节码中已经没有泛型的任何信