码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 猿创征文|Java-泛型(Generic)


    泛型(Generic)

    • 一、为什么要有泛型(Generic)?
      • 1、举例
      • 2、泛型的设计背景
      • 3、泛型比用Object存储数据的优点
    • 二、泛型的概念
    • 三、在集合中使用泛型
      • 1、代码演示1
      • 2、代码演示2
    • 四、自定义泛型结构
      • 1、泛型的声明
      • 2、泛型的实例化
      • 3、自定义泛型类、泛型接口
        • 3.1 语法格式
        • 3.2 注意
      • 4、自定义泛型方法
        • 4.1 泛型方法的格式
        • 4.2 注意
      • 5、泛型在继承上的体现
    • 五、通配符的使用
      • 1、使用类型通配符:?
      • 2、有限制的通配符
      • 3、代码演示
    • 六、泛型应用举例
      • 1、泛型嵌套
      • 2、实际案例

    一、为什么要有泛型(Generic)?

    1、举例

    ⭕ 泛型好比一个标签

    ⭕ 中药店,每个抽屉外面贴着标签

    ⭕ 超市购物架上很多瓶子,每个瓶子装的是什么,有标签

    2、泛型的设计背景

    集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection,List,ArrayList 中这个就是类型参数,即泛型。

    3、泛型比用Object存储数据的优点

    ⭕ 解决元素存储的安全性问题,好比商品、药品标签,不会弄错。

    ⭕ 解决获取数据元素时,需要类型强制转换的问题,好比不用每回拿商品、药品都要辨别。

    ⭕ Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮。

    在这里插入图片描述

    代码演示:

    @Test
        public void test1(){
            ArrayList list = new ArrayList();
            //需求:存放学生的成绩
            list.add(78);
            list.add(76);
            list.add(89);
            list.add(88);
            //问题一:类型不安全
    //        list.add("Tom");
    
            for(Object score : list){
                //问题二:强转时,如果前面执行了list.add("Tom")语句,则可能出现ClassCastException异常
                int stuScore = (Integer) score;
                System.out.println(stuScore);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    二、泛型的概念

    ⭕ 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。
    这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实 际的类型参数,也称为类型实参)。

    ⭕ 从JDK1.5以后,Java引入了“参数化类型(Parameterizedtype)”的概念,允许我们在创建集合时再指定集合元素的类型,正如:List,这表明该List只能保存字符串类型的对象。

    ⭕ JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持, 从而可以在声明集合变量、创建集合对象时传入类型实参。

    三、在集合中使用泛型

    1、代码演示1

      //在集合中使用泛型的情况:以ArrayList为例
        @Test
        public void test2(){
           ArrayList<Integer> list =  new ArrayList<Integer>();
    
            list.add(78);
            list.add(87);
            list.add(99);
            list.add(65);
            //编译时,就会进行类型检查,保证数据的安全
    //        list.add("Tom");
    
            //方式一:
    //        for(Integer score : list){
    //            //避免了强转操作
    //            int stuScore = score;
    //
    //            System.out.println(stuScore);
    //
    //        }
            //方式二:
            Iterator<Integer> iterator = list.iterator();
            while(iterator.hasNext()){
                int stuScore = iterator.next();
                System.out.println(stuScore);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    2、代码演示2

        //在集合中使用泛型的情况:以HashMap为例
         @Test
        public void test1(){
    //        Map map = new HashMap();
            //jdk7新特性:类型推断
            Map<String,Integer> map = new HashMap<>();
    
            map.put("Tom",87);
            map.put("Jerry",87);
            map.put("Jack",67);
    
    //        map.put(123,"ABC");
            //泛型的嵌套
            Set<Map.Entry<String,Integer>> entry = map.entrySet();
            Iterator<Map.Entry<String, Integer>> iterator = entry.iterator();
    
            while(iterator.hasNext()){
                Map.Entry<String, Integer> e = iterator.next();
                String key = e.getKey();
                Integer value = e.getValue();
                System.out.println(key + "----" + value);
            }
            /**
             * Tom----87
             * Jerry----87
             * Jack----67
             */
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    四、自定义泛型结构

    1、泛型的声明

    interface List 和 class GenTest ,其中,T,K,V不代表值,而是表示一种类型。这里使用任意字母都可以。常用T表示,是Type的缩写。

    2、泛型的实例化

    一定要在类名后面指定类型参数的值(类型)。如:

    List strList = new ArrayList();
    Iterator iterator = customers.iterator();

    3、自定义泛型类、泛型接口

    3.1 语法格式

    class Person<T> {
        // 使用T类型定义变量
        private T info;
        
        
        // 使用T类型定义一般方法
        public T getInfo() {
            return info; 
        }
        public void setInfo(T info) {
            this.info = info; 
        }
    
    
        // 使用T类型定义构造器
        public Person() {
        }
        public Person(T info) {
            this.info = info; 
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.2 注意

    ⭕ 把一个集合中的内容限制为一个特定的数据类型,这就是generics背后的核心思想 使用泛型的主要优点是能够在编译时而不是在运行时检测错误。

    ⭕ 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:

    ⭕ 泛型类的构造器如下:public GenericClass(){}。而下面是错误的:public GenericClass(){}

    ⭕ 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。

    ⭕ 泛型不同的引用不能相互赋值,尽管在编译时ArrayList和ArrayList是两种类型,但是,在运行时只有一个ArrayList被加载到JVM中。

    代码演示:

      @Test
        public void test3(){
            ArrayList<String> list1 = null;
            ArrayList<Integer> list2 = new ArrayList<Integer>();
            //泛型不同的引用不能相互赋值。
    //        list1 = list2;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ⭕ 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。
    经验:泛型使用要么一路都用。要不用,一路都不要用。

    代码演示:

    @Test
        public void test1(){
            //如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为Object类型
            //要求:如果大家定义了类是带泛型的,建议在实例化时要指明类的泛型。
            Order order = new Order();
            order.setOrderT(123);
            order.setOrderT("ABC");
    
            //建议:实例化时指明类的泛型
            Order<String> order1 = new Order<String>("orderAA",1001,"order:AA");
    
            order1.setOrderT("AA:hello");
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    ⭕ 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。

    ⭕ jdk1.7,泛型的简化操作:ArrayList flist = new ArrayList<>();

    ⭕ 由于子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不再需要指明泛型。

    代码演示:

    public class SubOrder extends Order<Integer> { }//SubOrder:不是泛型类
    public class SubOrder1<T> extends Order<T> { }//SubOrder1:仍然是泛型类
    
    
        @Test
        public void test2(){
            SubOrder sub1 = new SubOrder();
            //由于子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不再需要指明泛型。
            sub1.setOrderT(1122);
    
            SubOrder1<String> sub2 = new SubOrder1<>();
            sub2.setOrderT("order2...");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    ⭕ 泛型的指定中只能是类,不能使用基本数据类型,可以使用包装类替换。

    ⭕ 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。

    ⭕ 异常类不能是泛型的

    代码演示:

    //异常类不能声明为泛型类
    //public class MyException extends Exception{
    //}
    
    • 1
    • 2
    • 3

    ⭕ 不能使用new E<>。但是可以:E<> elements = (E<>)new Object;
    参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。

    ⭕ 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
    ● 子类不保留父类的泛型:按需实现

    1. 没有类型 擦除
    2. 具体类型

    ● 子类保留父类的泛型:泛型子类

    1. 全部保留
    2. 部分保留

    结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型

    代码演示:

    class Father<T1, T2> {
    }
    // 子类不保留父类的泛型
    // 1)没有类型 擦除
    class Son1 extends Father {// 等价于class Son extends Father{
    }
    // 2)具体类型
    class Son2 extends Father<Integer, String> {
    }
    // 子类保留父类的泛型
    // 1)全部保留
    class Son3<T1, T2> extends Father<T1, T2> {
    }
    // 2)部分保留
    class Son4<T2> extends Father<Integer, T2> {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    代码演示2:

    //自定义泛型结构:泛型类、泛型接口
    class Father<T1, T2> {
    }
    // 子类不保留父类的泛型
    // 1)没有类型 擦除
    class Son<A, B> extends Father{//等价于class Son extends Father{
    }
    // 2)具体类型
    class Son2<A, B> extends Father<Integer, String> {
    }
    // 子类保留父类的泛型
    // 1)全部保留
    class Son3<T1, T2, A, B> extends Father<T1, T2> {
    }
    // 2)部分保留
    class Son4<T2, A, B> extends Father<Integer, T2> {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    4、自定义泛型方法

    4.1 泛型方法的格式

    [访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常

    代码演示:

    public class DAO {
         public <E> E get(int id, E e) {
             E result = null;
             return result; 
         } 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4.2 注意

    🔺 泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。
    换句话说,泛型方法所属的类是不是泛型类都没有关系。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。

    🔺 泛型方法,可以声明为静态的。
    原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。

    代码演示:

    public static <E>  List<E> copyFromArrayToList(E[] arr){
    
            ArrayList<E> list = new ArrayList<>();
    
            for(E e : arr){
                list.add(e);
            }
            return list;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    5、泛型在继承上的体现

    ⭕ 如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G并不是G的子类型,二者是并列关系。
    比如:String是Object的子类,但是List并不是List的子类。

    代码演示:

        @Test
        public void test1(){
    
            Object obj = null;
            String str = null;
            obj = str;
    
            Object[] arr1 = null;
            String[] arr2 = null;
            arr1 = arr2;
            //编译不通过
    //        Date date = new Date();
    //        str = date;
            List<Object> list1 = null;
            List<String> list2 = new ArrayList<String>();
            //此时的list1和list2的类型不具有子父类关系
            //编译不通过
    //        list1 = list2;
            /*
            反证法:
            假设list1 = list2;
               list1.add(123);导致混入非String的数据。出错。
    
             */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在这里插入图片描述

    五、通配符的使用

    1、使用类型通配符:?

    ⭕ 比如:List ,Map
    List是List、List等各种泛型List的父类。

    ⭕ 读取List的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。

    ⭕ 将任意元素加入到其中不是类型安全的:因为我们不知道c的元素类型,我们不能向其中添加对象。add()方法有类型参数E作为集合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。唯一的例外的是null,它是所有类型的成员。

    Collection<?> c = new ArrayList<String>();
    c.add(new Object()); // 编译时错误
    
    • 1
    • 2

    ⭕ 另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object。

    ⭕ 不能用在泛型方法声明上,返回值类型前面<>不能使用?

    public static <?> void test(ArrayList<?> list){ }//编译错误
    
    • 1

    ⭕ 不能用在泛型类的声明上

    class GenericTypeClass<?>{ }//编译错误
    
    • 1

    ⭕ 不能用在创建对象上,右边属于创建集合对象

    ArrayList<?> list2 = new ArrayList<?>();//编译错误
    
    • 1

    2、有限制的通配符

    ⭕ 允许所有泛型的引用调用

    ⭕ 通配符指定上限 上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=

     <? extends Number>// (无穷小 , Number]只允许泛型为Number及Number子类的引用调用
    
    • 1

    ⭕ 通配符指定下限 下限super:使用时指定的类型不能小于操作的类,即>=

    <? super Number> //[Number , 无穷大) 只允许泛型为Number及Number父类的引用调用
    
    • 1

    ⭕
    只允许泛型为实现Comparable接口的实现类的引用调用

    3、代码演示

     @Test
        public void test4(){
    
            List<? extends Person> list1 = null;
            List<? super Person> list2 = null;
    
            List<Student> list3 = new ArrayList<Student>();
            List<Person> list4 = new ArrayList<Person>();
            List<Object> list5 = new ArrayList<Object>();
    
            list1 = list3;
            list1 = list4;
    //        list1 = list5;
    
    //        list2 = list3;
            list2 = list4;
            list2 = list5;
    
            //读取数据:
            list1 = list3;
            Person p = list1.get(0);
            //编译不通过
            //Student s = list1.get(0);
    
            list2 = list4;
            Object obj = list2.get(0);
            编译不通过
    //        Person obj = list2.get(0);
    
            //写入数据:
            //编译不通过
    //        list1.add(new Student());
    
            //编译通过
            list2.add(new Person());
            list2.add(new Student());
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    六、泛型应用举例

    1、泛型嵌套

    public static void main(String[] args) {
    HashMap<String, ArrayList<Citizen>> map = new HashMap<String, ArrayList<Citizen>>();
    ArrayList<Citizen> list = new ArrayList<Citizen>();
    list.add(new Citizen("刘恺威"));
    list.add(new Citizen("杨幂"));
    list.add(new Citizen("小糯米"));
    map.put("刘恺威", list);
    Set<Entry<String, ArrayList<Citizen>>> entrySet = map.entrySet();
    Iterator<Entry<String, ArrayList<Citizen>>> iterator = entrySet.iterator();
    while (iterator.hasNext()) {
    Entry<String, ArrayList<Citizen>> entry = iterator.next();
    String key = entry.getKey();
    ArrayList<Citizen> value = entry.getValue();
    System.out.println("户主:" + key);
    System.out.println("家庭成员:" + value);
    } }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2、实际案例

    用户在设计类的时候往往会使用类的关联关系,例如,一个人中可以定义一个信息的属性,但是一个人可能有各种各样的信息(如联系方式、基本信息等),所以此信息属性的类型就可以通过泛型进行声明,然后只要设计相应的信息类即可。

    >

  • 相关阅读:
    star 最多的 Go 语言本地化库|GitHub 2.8K
    企业工程项目管理系统源码(三控:进度组织、质量安全、预算资金成本、二平台:招采、设计管理)
    带你十天轻松搞定 Go 微服务系列(九、链路追踪)
    C ssh2 执行命令
    通信总线协议四 :SPI
    无涯教程-JavaScript - SUMX2PY2函数
    【电源专题】开关电源中的电感电流测量
    电脑主机如何选择内存条
    吃透底层:从路由到前缀树
    动态规划 Ⅱ
  • 原文地址:https://blog.csdn.net/weixin_52533007/article/details/126565904
    • 最新文章
    • 攻防演习之三天拿下官网站群
      数据安全治理学习——前期安全规划和安全管理体系建设
      企业安全 | 企业内一次钓鱼演练准备过程
      内网渗透测试 | Kerberos协议及其部分攻击手法
      0day的产生 | 不懂代码的"代码审计"
      安装scrcpy-client模块av模块异常,环境问题解决方案
      leetcode hot100【LeetCode 279. 完全平方数】java实现
      OpenWrt下安装Mosquitto
      AnatoMask论文汇总
      【AI日记】24.11.01 LangChain、openai api和github copilot
    • 热门文章
    • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
      奉劝各位学弟学妹们,该打造你的技术影响力了!
      五年了,我在 CSDN 的两个一百万。
      Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
      面试官都震惊,你这网络基础可以啊!
      你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
      心情不好的时候,用 Python 画棵樱花树送给自己吧
      通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
      13 万字 C 语言从入门到精通保姆级教程2021 年版
      10行代码集2000张美女图,Python爬虫120例,再上征途
    Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
    正则表达式工具 cron表达式工具 密码生成工具

    京公网安备 11010502049817号