码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 11.泛型


    文章目录

    • 1 泛型概念
    • 2. 自定义泛型结构
    • 3 泛型方法
    • 4 泛型在继承上的体现
    • 5 通配符的使用

    1 泛型概念

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

    泛型的优势:

    1. 类型安全性: 泛型允许你在编译时检查类型的一致性。通过指定类型参数,你可以确保代码在运行时不会发生类型转换错误。 (没有泛型的时候用Object,类型太多,有了泛型就能在简化代码的同时规定类型)
    2. 代码重用: 泛型使得你可以编写通用的代码,可以用于多种不同类型的对象,而无需重复编写相似的代码。这样可以减少代码的冗余,提高代码的可维护性和可读性。
    3. 性能优化: 泛型在编译时执行类型检查,而不是在运行时进行。这意味着在运行时不需要额外的类型检查和转换操作,可以提高程序的性能。

    2. 自定义泛型结构

    1. 泛型的声明 interface List 和 class GenTest

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

    2. 泛型的实例化:

      当泛型定义的接口或者类实例化时指定泛型具体类型的过程就是泛型的实例化

      List strList = new ArrayList();

      Iterator iterator = customers.iterator();

    泛型使用细节:

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

    2. 泛型类的构造器定义方式:public GenericClass(){}。而非:public GenericClass(){};

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

      class GenericTest {
      	public static void main(String[] args) {
          // 1、使用时:类似于Object,不等同于Object
          ArrayList list = new ArrayList();
          // list.add(new Date());//有风险
          list.add("hello");
          test(list);// 泛型擦除,编译不会类型检查
              
          // ArrayList list2 = new ArrayList();
          // test(list2);//一旦指定Object,编译会类型检查,必须按照Object处理,此处报错 
          //java: 不兼容的类型: java.util.ArrayList无法转换为java.util.ArrayList
          }
          
          public static void test(ArrayList<String> list) {
              String str = "";
              for (String s : list) {
              	str += s + ",";
              }
              System.out.println("元素:" + str);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
    4. 泛型的简化操作:ArrayList flist = new ArrayList<>()类型推断

    5. 泛型的指定中不能使用基本数据类型,可以使用包装类替换

    6. 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型,但允许使用和类无关的泛型也就是泛型方法。(因为静态方法早于类实例的创建,并且允许未实例化的类直接调用静态方法,此时泛型未被实例化)

    7. 异常类不能是泛型的

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

    9. 父类有泛型,子类可以保留父类泛型、泛型实例化、或者增加泛型等等

      子类不保留父类的泛型:按需实现

      • 没有类型 擦除
      • 具体指定泛型类型

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

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

      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> {}
      // 3)甚至可以增加泛型
      class Son5<T2,T3> extends Father<Integer, T2> {}
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16

      3 泛型方法

      方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的 类型。

      泛型方法的格式:(访问权限后面定义泛型)

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

      例子一:

      public static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
          for (T o : a) {
          	c.add(o);
          }
      }
      
      public static void main(String[] args) {
          Object[] ao = new Object[100];
          Collection<Object> co = new ArrayList<Object>();
          fromArrayToCollection(ao, co);
          
          String[] sa = new String[20];
          Collection<String> cs = new ArrayList<>();
          fromArrayToCollection(sa, cs);
          
          Collection<Double> cd = new ArrayList<>();
          // 下面代码中T是Double类,但sa是String类型,编译错误。
          // fromArrayToCollection(sa, cd);
          // 下面代码中T是Object类型,sa是String类型,可以赋值成功。
          fromArrayToCollection(sa, co);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21

      例子二:

      class Creature{}
      class Person extends Creature{}
      class Man extends Person{}
      
      class PersonTest {
          public static <T extends Person> void test(T t){//要求是Person的子类
              System.out.println(t);
          }
          
          public static void main(String[] args) {
          	test(new Person());
          	test(new Man());
          	//The method test(T) in the type PersonTest is not 
          	//applicable for the arguments (Creature)
          	test(new Creature());//报错!!
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17

      4 泛型在继承上的体现

      如果B是A的一个子类(子类或者子接口),而G是具有泛型声明的类或接口,G并不是G的子类型!若A是B的子类,则A是B的子类

      比如:String是Object的子类,但是List并不是List的子类。

      public void testGenericAndSubClass() {
          Person[] persons = null;
          Man[] mans = null;
          // 而 Person[] 是 Man[] 的父类.
          persons = mans;
          Person p = mans[0];
          
          // 在泛型的集合上
          List<Person> personList = null;
          List<Man> manList = null;
          // personList = manList;(报错)
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      5 通配符的使用

      在Java中,类型通配符(Wildcard)是用来表示未知类型的,通常在泛型中使用。它的语法形式是?,可以用在泛型类、泛型方法、以及泛型接口中。

      类型通配符有两种常见的形式:

      1. ? extends T: 表示通配符的类型是T或者T的子类型。这个通配符意味着你可以传递T或者T的任何子类型作为参数,但是不能传递T的超类型。

        List<? extends Number> list = new ArrayList<>();
        
        • 1

        这里list可以指向任何元素是Number或者Number的子类的列表。

      2. ? super T: 表示通配符的类型是T或者T的父类型。这个通配符意味着你可以传递T或者T的任何父类型作为参数,但是不能传递T的子类型。

        List<? super Integer> list = new ArrayList<>();
        
        • 1

        这里list可以指向任何元素是Integer或者Integer的父类的列表。

        public static void printCollection3(Collection<? extends Person> coll) {
            //Iterator只能用Iterator或Iterator.why?
            Iterator<?> iterator = coll.iterator();
            while (iterator.hasNext()) {
          	    System.out.println(iterator.next());
            }
        }
        public static void printCollection4(Collection<? super Person> coll) {
            //Iterator只能用Iterator或Iterator.why?
            Iterator<?> iterator = coll.iterator();
            while (iterator.hasNext()) {
        		System.out.println(iterator.next());
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14

      使用类型通配符可以增加泛型的灵活性,特别是在处理泛型集合时,因为它允许你编写更通用的代码,而不需要明确知道泛型参数的具体类型。

      细节:

      1. 使用类型通配符:?

        比如:List ,Map

        List 是 List、List等各种泛型List的父类。

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

      3. 不能向List中添加元素,因为不知道他的数据类型。但null除外,可以向其添加null值。读取元素正常,读取出来的元素都是Object类型

        将任意元素加入到其中不是类型安全的:

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

        因为我们不知道c的元素类型,我们不能向其中添加对象。唯一可以添加的数值式null,另一方面,我们可以调用get()方法并使用其返回值,返回值是一个位置的类型,但是我们知道,他总是一个Object

      4. 例子:

        public static void main(String[] args) {
            List<?> list = null;
            list = new ArrayList<String>();
            list = new ArrayList<Double>();
        
            // list.add(3);//编译不通过使用通配符不能添加元素
            list.add(null);
            List<String> l1 = new ArrayList<String>();
            List<Integer> l2 = new ArrayList<Integer>();
            l1.add("尚硅谷");
            l2.add(15);
            read(l1);
            read(l2);
        }
        
        public static void read(List<?> list) {
            for (Object o : list) {
            System.out.println(o);
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19

        注意点:

        //注意点1:编译错误:不能用在泛型方法声明上,返回值类型前面<>不能使用?
        public static <?> void test(ArrayList<?> list){
        }
        //注意点2:编译错误:不能用在泛型类的声明上
        class GenericTypeClass<?>{
        }
        //注意点3:编译错误:不能用在创建对象上,右边属于创建集合对象
        ArrayList<?> list2 = new ArrayList<?>();
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8

        ​

      5. 相关阅读:
        数据结构——排序算法——希尔排序
        Django笔记十三之select_for_update等选择和更新等相关操作
        批量提取 srt 字幕文件中的文字
        Visual Components软件有哪些用途 衡祖仿真
        svn多仓库权限控制
        Linux 驱动PCIE编程接口
        QPushButton样式设置
        推荐算法 面试
        git init报错:‘git‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。
        MySQL主从复制与读写分离
      6. 原文地址:https://blog.csdn.net/weixin_63267854/article/details/138172010
        • 最新文章
        • 攻防演习之三天拿下官网站群
          数据安全治理学习——前期安全规划和安全管理体系建设
          企业安全 | 企业内一次钓鱼演练准备过程
          内网渗透测试 | 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号