• Java泛型


    目录

    一、泛型是一种约束

    1. 泛型

    2. 泛型的优点

    二、泛型的使用

    1. 泛型的注意事项

    2. 泛型的语法

    泛型类

    泛型接口

    泛型方法

    三、泛型通配符

    1. 什么是类型通配符

    2.  类型通配符的上限

    3. 类型通配符的下限

    4. 代码示例

    四、类型擦除

    1. 代码验证

    2. 无限制类型擦除:把T转换成Object

    3. 有限制类型擦除:把T转换成上限

    4. 擦除方法

    如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏哦


    一、泛型是一种约束

    JAVA推出泛型以前,程序员可以构建一个元素类型为Object的集合,该集合能够存储任意的数据类型对象,而在使用该集合的过程中,需要程序员明确知道存储每个元素的数据类型,否则很容易引发ClassCastException异常。

    1. 泛型

    Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构

    泛型的本质就是参数化类型(类型参数化),也就是所操作的数据类型被指定为一个参数。

    1. private static void m2() {
    2. ArrayList list=new ArrayList<>();
    3. list.add("333");
    4. list.add("string");
    5. //list.add(88); 编译期检查
    6. for (String s : list) {
    7. System.out.println(s); //减少了数据类型转换
    8. }
    9. }
    10. /**
    11. * 未使用泛型之前
    12. */
    13. private static void m1() {
    14. ArrayList objects = new ArrayList();
    15. objects.add("111");
    16. objects.add(88);
    17. objects.add(true);
    18. for (Object object : objects) {
    19. System.out.println(object);
    20. }
    21. }

    2. 泛型的优点

    1.类型安全,编译期检查

    2.消除了强制类型的转换、提高效率


    二、泛型的使用

    1. 泛型的注意事项

    • 泛型类,如果没有指定具体的数据类型,此时,操作类型是Object
    • 泛型的类型参数只能是类类型,不能是基本数据类型
    • 泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型
    • 泛型的使用,类型参数化,泛型标识,由外部使用类来指定
    • 可以结合后面的泛型擦除理解,泛型擦除之后 Generic < String > 和Generic< Integer >都是Generic类。

    2. 泛型的语法

    泛型类

    T:type

    E:element

    K: key

    V :value

    ?:表示不确定的java类型

    一般是单个大写字母
    1. /**
    2. * 泛型的使用,类型参数化
    3. * T:泛型标识,由外部使用类来指定
    4. * @author Promsing(张有博)
    5. * @version 1.0.0
    6. * @since 2022/8/19 - 10:50
    7. */
    8. public class Generics {
    9. private T key;
    10. public Generics() {
    11. }
    12. public Generics(T key) {
    13. this.key = key;
    14. }
    15. public T getKey() {
    16. return key;
    17. }
    18. public void setKey(T key) {
    19. this.key = key;
    20. }
    21. }
    22. public static void main(String[] args) {
    23. //泛型类在创建对象的时候,来指定具体的数据类型
    24. //泛型极大的提高了代码的复用性
    25. Generics generics=new Generics<>();
    26. generics.setKey("aba");
    27. String key = generics.getKey();
    28. System.out.println(key);
    29. //泛型类在创建对象的时候,没有指定类型,默认使用Object
    30. Generics g=new Generics();
    31. //泛型不支持基本数据类型
    32. Generics g1=new Generics();
    33. System.out.println(g1.getClass()==(generics).getClass());//同一个字节码文件
    34. }
    • 子类也是泛型类,子类和父类的泛型类型要一致
      class ChildGeneric extends Generic
    • 子类不是泛型类,父类要明确泛型的数据类型
      class ChildGeneric extends Generic
    • 泛型是从子到父,向上传递

    父类

    1. /**
    2. * 泛型父类
    3. *
    4. * @author Promsing(张有博)
    5. * @version 1.0.0
    6. * @since 2022/8/19 - 11:17
    7. */
    8. public class Parent {
    9. private T key;
    10. public Parent() {
    11. }
    12. public Parent(T key) {
    13. this.key = key;
    14. }
    15. public T getKey() {
    16. return key;
    17. }
    18. public void setKey(T key) {
    19. this.key = key;
    20. }
    21. }

    子类*2

    1. /**
    2. * 子类要与父类的泛型标识一样
    3. * 子类也是泛型类,子类和父类的泛型类型要一致
    4. * 子类可以泛型拓展
    5. * @author Promsing(张有博)
    6. * @version 1.0.0
    7. * @since 2022/8/19 - 11:19
    8. */
    9. public class ChildFirst extends Parent{
    10. //ChildFirst
    11. }
    12. /**
    13. * 子类不是泛型类,父类要明确泛型的数据类型
    14. * 泛型是从子到父,向上传递
    15. * @author Promsing(张有博)
    16. * @version 1.0.0
    17. * @since 2022/8/19 - 11:25
    18. */
    19. public class ChildSecond extends Parent{
    20. }

    泛型接口

    语法

    1. interface 接口名称 <泛型标识,泛型标识,…> {
    2. 泛型标识 方法名();
    3. .....
    4. }
    • 实现类也是泛型类,实现类和接口的泛型类型要一致
    • 实现类不是泛型类,接口要明确数据类型

    泛型方法

    1.用法

    泛型方法是在调用方法的时候指明泛型的具体类型。

    2.语法:

    1. 修饰符 返回值类型 方法名(形参列表) {
    2. 方法体...
    3. }

    3.说明:

    • public与返回值中间非常重要,可以理解为声明此方法为泛型方法。
    • 只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法
    • < T >表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
    • 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
    • 泛型方法的调用,类型是调用方法的时候指定的
    1. /**
    2. * 普通方法:泛型类的成员方法
    3. */
    4. public T getKey() {
    5. return key;
    6. }
    7. /**
    8. * 普通方法:泛型类的成员方法
    9. */
    10. public void setKey(T key) {
    11. this.key = key;
    12. }
    13. /**
    14. * 定义泛型方法
    15. * @param list 参数
    16. * @param 泛型标识,具体类型,有调用方法的时候来指定
    17. * @return 结果
    18. */
    19. public E getRecord(ArrayList list){
    20. Random r=new Random();
    21. return list.get( r.nextInt(list.size()));
    22. }
    23. /**
    24. * 静态泛型方法
    25. */
    26. public static void getRecord(E e,K k,V v){
    27. System.out.println("e: "+e);
    28. System.out.println("k: "+k);
    29. System.out.println("v: "+v);
    30. }

    泛型方法与普通方法的区别:

    1.泛型方法可以用static修饰,普通方法不能使用static修饰。

    2.泛型方法是调用时才指定类型,普通方法要跟从类,指定类型

    3.泛型方法能够是方法独立于类而产生变化,更加灵活


    三、泛型通配符

    1. 什么是类型通配符

    类型通配符一般是使用"?"代替具体的类型实参。

    所以,类型通配符是类型实参,而不是类型形参。

    如何理解这里说的实参呢,因为泛型是将类型参数化了,所以类型通配符是类型实参

    2.  类型通配符的上限

    语法:

    类/接口

    要求该泛型的类型,只能是实参类型,或实参类型的子类类型。

    规定参数继承自 实参类型

    上限通配符,集合 不能填充元素

    3. 类型通配符的下限

    语法:

    类/接口

    要求该泛型的类型,只能是实参类型,或实参类型的父类类型。

    下限通配符,集合 可以填充元素

    4. 代码示例

    1. public class Box {
    2. private E first;
    3. public E getFirst() {
    4. return first;
    5. }
    6. public void setFirst(E first) {
    7. this.first = first;
    8. }
    9. }
    10. /**
    11. * 通配符:表示任意类型
    12. * @param box
    13. */
    14. public static void showBox1(Box box){
    15. Object first = box.getFirst();
    16. System.out.println(first);
    17. }
    18. /**
    19. * 上限通配符 规定参数继承自 Number,想想多态
    20. * @param box
    21. */
    22. public static void showBox(Box box){
    23. //上限通配符,集合 不能填充元素
    24. Number first = box.getFirst();
    25. System.out.println(first);
    26. }
    27. /**
    28. * 下限通配符 ,规定只能是Number或者Number的父类
    29. * @param box
    30. */
    31. public static void showBox2(Boxsuper Number> box){
    32. //下限通配符,集合 可以填充元素
    33. Object first = box.getFirst();
    34. System.out.println(first);
    35. }

    四、类型擦除

    泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是泛型代码能够很好地和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为–类型擦除。

    1. 代码验证

    1. public static void main(String[] args) {
    2. ArrayList strList = new ArrayList<>();
    3. ArrayList intList = new ArrayList<>();
    4. System.out.println(strList.getClass().getSimpleName());
    5. System.out.println(intList.getClass().getSimpleName());
    6. //发生 类型擦除了
    7. System.out.println("-------------");
    8. // 使用反射验证
    9. Erasure erasure=new Erasure<>();
    10. Field[] declaredFields = erasure.getClass().getDeclaredFields();
    11. for (Field declaredField : declaredFields) {
    12. System.out.println(declaredField);
    13. //private java.lang.Object com.promsing.generics.demo7.Erasure.key
    14. System.out.println(declaredField.getName());
    15. //key
    16. System.out.println(declaredField.getType().getName());
    17. //java.lang.Object
    18. }
    19. }

    2. 无限制类型擦除:把T转换成Object

    3. 有限制类型擦除:把T转换成上限

    4. 擦除方法

    如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏

  • 相关阅读:
    隐藏底部任务栏图标的方法
    数据结构复盘——第五章:树
    常用的数据库类别及介绍
    【 OpenGauss源码学习 —— 列存储(CStoreAllocator 类)】
    不相交集类 (并查集)
    后台管理---新建编辑优化
    Spring Boot 中使用 tkMapper
    LeetCode707:设计链表
    【深度学习-注意力机制attention 在seq2seq中应用】
    【文件搜索项目】使用jdbc操作SQLite
  • 原文地址:https://blog.csdn.net/promsing/article/details/126433520