• Java泛型


    一.泛型的概述

    ​ 泛型是java 1.5的新特性,本质是参数化类型,就是将要操作的数据类型指定为一个参数。泛型可以使用在类,接口,方法中,分别叫做泛型类,泛型接口以及泛型方法。

    ​ 比如ArrayList list = new ArrayList<>()就是一个泛型类

    ​ 为什么要使用泛型?

    ​ 在没有泛型之前,将数据存入集合,是这样子操作的:

    public class GenericTest {
        public static void main(String[] args) {
            ArrayList array = new ArrayList();
    
            array.add("Hello");
            array.add(123);
    
            String str = (String)array.get(0);
            int num = (int)array.get(1);
    
            System.out.println(str);
            System.out.println(num);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    ​ 存入数据的时候是可以任意类型,取出来时需要做一个类型转换,这样子操作就存在一个问题,过了许久后可能会忘记了集合的第几个数据是什么类型,一旦搞错,就会产生一个异常java.lang.ClassCastException

    ​ 引入泛型之后,代码就变成了这样子的:

    public class GenericTest {
        public static void main(String[] args) {
            ArrayList<String> array = new ArrayList();
    
            array.add("Hello");
            // array.add(123);
    
            String str = array.get(0);
            // int num = (int)array.get(1);
    
            System.out.println(str);
            // System.out.println(num);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    ​ 在编译时就已经确定了参数类型为String,如果存入集合的数据不是String,在编译时就会报错,在取出数据时也不需要进行类型转换了,这样一来,使得在运行时可能发生的java.lang.ClassCastException异常在编译时期就能被编译器发现,从而从运行时错误变成了编译时期的错误,并且在使用的时候避免了类型转换的麻烦。

    二.泛型的定义

    1. 定义泛型类的格式

      修饰符 class 类名<代表泛型的变量> { ... }

      比如源码中的ArrayList的定义是这样的:

      public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
      
      {
      	....
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      泛型类的编写比普通类要稍微复杂一些,我们可以根据普通类的编写来进行改造从而完成泛型类的编写。

      比如先创建一个普通类Person

      public class Person {
          private String name;
          
          public Person(String name) {
              this.name = name;
          }
      
          public String getName() {
              return this.name;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

      然后使用T来标记需要指定的类型,这里是String,改写后的Person类如下:

      public class Person<T> {
          private T name;
          
          public Person(T name) {
              this.name = name;
          }
      
          public T getName() {
              return this.name;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    2. 定义泛型方法的格式

      修饰符 <代表泛型的变量> 返回值类型 方法名(参数) { ... }

      比如可以在普通类中定义泛型方法:

      public class Person {
          private String name;
          
          public Person(String name) {
              this.name = name;
          }
      
          public String getName() {
              return this.name;
          }
      
          public <T> void print (T msg) {
              System.out.println(msg.getClass());
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
    3. 定义泛型接口的格式

      修饰符 interface 接口名<代表泛型的变量> { ... }

      比如:

      public interface List<E> {
          ....
      }
      
      • 1
      • 2
      • 3
    4. 泛型类、泛型方法和泛型接口的确定时间

      上面介绍了如何定义泛型,但是这个泛型在什么时候确定泛型类型呢?

      对于泛型类,是在创建对象时候确定泛型类型的,比如:``ArrayList list = new ArrayList<>()`

      对于泛型方法,是在方法被调用的时候确定泛型类型的,比如Person p = new Person(); p.print("hello java");

      对于泛型接口,是有两种情况存在的:

      第一种:在实现接口时候指定泛型类型。

      第二种:如果实现接口的时候仍旧使用了泛型,则在创建接口实现类对象时候指定泛型类型。

    5. 泛型的定义需要注意的几点

      • 泛型的类型只能是引用类型,不可以是基本类型

      • <代表泛型的变量> 尖括号里可以使用任意的字母,对编译器来说都是一样的,但是习惯上使用T,E,K,V等字母来表示(完全是因为程序员习惯)

        其中主要是因为这些字母都代表了一个含义:

        • **T :Type(类型) **
        • E:Element(元素)
        • K:Key(键)
        • V:Value(值)

    三.泛型的通配符

    ​ 泛型有三种类型的通配符:

    • :上边界通配符

      意义:表示只能接受T类型或者T类型的子类型

      缺陷:只能读取对象,不能添加任何对象(null除外)

    • :下边界通配符

      意义:表示只能接受T类型或者T类型的父类型

      缺陷:只能添加对象,不能读取对象(Object除外)

    • :无界通配符

      等同于:

    比如有Object类,String类,Number类,Integer类,其中Number类是Integer类的父类

    // 泛型的上边界,此时泛型?,必须是Number或者其子类
    public static void getElement1(Collection<? extends Number> c) {}
    // 泛型的下边界,此时泛型?,必须是Number或者其父类
    public static void getElement2(Collection<? super Number> c) {}
    
    public static void main(String[] args) {
        Collection<Integer> list1 = new ArrayList<Integer>();
        Collection<String> list2 = new ArrayList<String>();
        Collection<Number> list3 = new ArrayList<Number>();
        Collection<Object> list4 = new ArrayList<Object>();
        
        getElement1(list1);	
        getElement1(list2);	// error
        getElement1(list3);
        getElement1(list4);	// error
        
        getElement2(list1); // error
        getElement2(list2); // error
        getElement2(list3);
        getElement2(list4);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    流行语来源查询易语言代码
    Java基础知识之数组、集合类、多线程、文件处理
    RabbitMQ持久化
    【scikit-learn基础】--『监督学习』之 逻辑回归分类
    解读两篇最新多元时间序列预测工作
    图书馆座位预约系统管理/基于微信小程序的图书馆座位预约系统
    SpringBoot整合MQTT
    Adobe XD最新版号查询,如何使用?
    Go语言类库-reflect(反射)
    swift-类结构源码探寻(一)
  • 原文地址:https://blog.csdn.net/chisuisi5702/article/details/126284914