前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Java 泛型】泛型用法 ( 泛型编译期擦除 | 上界通配符 <? extends T> | 下界通配符 <? super T> )

【Java 泛型】泛型用法 ( 泛型编译期擦除 | 上界通配符 <? extends T> | 下界通配符 <? super T> )

作者头像
韩曙亮
发布2023-03-29 16:48:02
8990
发布2023-03-29 16:48:02
举报
文章被收录于专栏:韩曙亮的移动开发专栏

文章目录

一、泛型擦除


泛型只保留到 编译期 , 在 编译完毕后 , 泛型就不存在了 ;

在运行时 , 通过反射 , 调用泛型类 , 即使违反了泛型规则 , 也能进行相关操作 , 这是因为 在运行时 , 已经没有泛型相关的限制 , 泛型限制在编译时就已经被擦除了 ;

但是 泛型的信息 , 保存在了常量表中 , 仍然可以获取到 ;

泛型擦除 是为了 泛型可以兼容 老版本的 JDK 而设计的 ,

泛型是 JDK 5 加入的 , 如果添加了泛型 , 导致字节码的格式改变 , 必然导致之前版本的 JDK 无法运行有泛型的字节码程序 ;

给定一个 Demo.java 类 , 其中定义了一个 <T> T get() 方法 ;

代码语言:javascript
复制
public class Demo {
    public <T> T get(){
        T t = null;
        return t;
    }
}

将其编译后 , 查看字节码附加信息 ;

代码语言:javascript
复制
D:\java>javap -v Demo.class
Classfile /D:/java/Demo.class
  Last modified 2021-9-7; size 307 bytes
  MD5 checksum 727bc59421b23a5f0a31af0e91630ab8
  Compiled from "Demo.java"
public class Demo
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #3.#14         // java/lang/Object."<init>":()V
   #2 = Class              #15            // Demo
   #3 = Class              #16            // java/lang/Object
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               get
   #9 = Utf8               ()Ljava/lang/Object;
  #10 = Utf8               Signature
  #11 = Utf8               <T:Ljava/lang/Object;>()TT;
  #12 = Utf8               SourceFile
  #13 = Utf8               Demo.java
  #14 = NameAndType        #4:#5          // "<init>":()V
  #15 = Utf8               Demo
  #16 = Utf8               java/lang/Object
{
  public Demo();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public <T extends java.lang.Object> T get();
    descriptor: ()Ljava/lang/Object;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=1
         0: aconst_null
         1: astore_1
         2: aload_1
         3: areturn
      LineNumberTable:
        line 3: 0
        line 4: 2
    Signature: #11                          // <T:Ljava/lang/Object;>()TT;
}
SourceFile: "Demo.java"

Demo 中的 get 方法类型返回值是 Ljava/lang/Object , 不是泛型 T , 这就是泛型在字节码中被擦除了 ;

代码语言:javascript
复制
descriptor: ()Ljava/lang/Object;

执行下面的代码 , 在运行时 , 使用反射 , 向 list1 集合中添加 int 类型的元素 , 添加成功 ; 说明在运行时 , 不再进行泛型检查 , 即使不符合泛型要求 , 也能操作成功 ;

代码语言:javascript
复制
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // 编译器 在 编译期 进行检查
        List<String> list1 = new ArrayList<>();
        //list1.add(1);

        // 编译器 在 编译期 不进行检查
        List list2 = new ArrayList<String>();
        //list2.add(1);

        try {
            Method method = ArrayList.class.getMethod("add", Object.class);
            method.invoke(list1, 1);

            System.out.println("list1 集合大小 : " + list1.size());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

执行结果 :

二、泛型的上界通配符 <? extends T>


泛型 上界通配符 : <T extends Person> 表示泛型 T 是 Person 的子类 , <? extends T> 泛型类型 只能是 T 的子类 ;

  • <T extends Person> 只能在声明泛型时使用 , 不能在 使用 泛型 时使用 ;
代码语言:javascript
复制
public class Data<T extends Person> {
}
  • <? extends T> 只能在使用 泛型 时使用 , 不能在声明 泛型类/方法 时 使用 ;
代码语言:javascript
复制
Data<? extends Person> data6 = new Data<>();
3

个有继承关系的类 :

代码语言:javascript
复制
public class Animal {
}
代码语言:javascript
复制
public class Person extends Animal {
}
代码语言:javascript
复制
public class Woman extends Person {
}

泛型类 : 该泛型类接收一个泛型 , 该泛型 T 是 Person 的子类 ;

代码语言:javascript
复制
public class Data<T extends Person> {
}

main 函数 :

代码语言:javascript
复制
        // 上边界通配符
        // 符合要求 , 可设置 Woman 的父类 , Woman 以及 Person 类
        Data<? super Woman> data4 = new Data<>();

        // 符合要求 , 可设置 Person 类
        Data<? super Person> data5 = new Data<>();

        // 不符合要求 , Animal 是最顶层的类 , 其取值都不符合 <T extends Person> 要求
        Data<? super Animal> data6 = new Data<>();

报错信息 : 传入不符合要求的泛型的报错信息 ;

三、泛型的下界通配符 <? super T>


泛型 下界通配符 : <? super T> 泛型类型 只能是 T 的父类 ;

  • <? super T> 只能在使用 泛型 时使用 , 不能在声明 泛型类/方法 时 使用 ;
代码语言:javascript
复制
Data<? super Person> data6 = new Data<>();
3

个有继承关系的类 :

代码语言:javascript
复制
public class Animal {
}
代码语言:javascript
复制
public class Person extends Animal {
}
代码语言:javascript
复制
public class Woman extends Person {
}

泛型类 : 该泛型类接收一个泛型 , 该泛型 T 是 Person 的子类 ;

代码语言:javascript
复制
public class Data<T extends Person> {
}

main 函数 :

代码语言:javascript
复制
        // 上边界通配符
        // 符合要求 , 可设置 Woman 的父类 , Woman 以及 Person 类
        Data<? super Woman> data4 = new Data<>();

        // 符合要求 , 可设置 Person 类
        Data<? super Person> data5 = new Data<>();

        // 不符合要求 , Animal 是最顶层的类 , 其取值都不符合 <T extends Person> 要求
        //Data<? super Animal> data6 = new Data<>();

报错信息 : 不符合要求 , Animal 是最顶层的类 , 其取值都不符合 要求

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-09-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 一、泛型擦除
  • 二、泛型的上界通配符 <? extends T>
  • 三、泛型的下界通配符 <? super T>
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档