Java泛型笔记

1 泛型概述

1.1 背景

  • Java推出泛型之前,可以构建一个存储Object类型的集合,该集合能够存储任意类型,但在取出元素的过程中,需要程序员明确知道每个元素的类型,否则容易引发类型转换异常。
ArrayList list = new ArrayList();
list.add("111");
list.add(100);
list.add(true);

for(int i = 0; i < list.size(); i++){
    Object o = list.get(i);
    //100和true无法转换,会出现异常
    String str = (String)o;
    System.out.println(str);
}

1.2 泛型的概念

  • JDK 5后引入的新特性

  • 提供编译时类型安全检测机制,即能在编译时就检测到非法类型数据

  • 本质是参数化类型,即所操作的数据类型也被指定为一个参数

1.3 泛型基础使用

  • 对于基本类型,也要使用Class类来表示
ArrayList list = new ArrayList<>();//用Integer而不是int
  • Java 1.7版本后面的<>可以不加

1.4 基本好处

  • 类型安全
  • 消除了强制类型转换

2 泛型类与泛型接口

2.1 基本泛型类

2.1.1 泛型类定义语法

//泛型标识一般用T, E, K, V
class 类名<泛型标识1, 泛型标识2...>{
    private 泛型标识1 变量1;
    ...
}

2.1.2 为什么基本类型不能作为泛型标识

  • 因为基本类型如int不是继承自Object类的,而其他所有类的基类都是Object
  • 编译的时候,各类泛型会也会转换成Object类型来处理,int不属于Object所以不能用

2.1.3 同一泛型类,本质上是去除泛型的同一类型

class MyClass{
    private T a;
}
MyClass myClass1 = new MyClass();
MyClass myClass2 = new MyClass();
System.out.println(myClass1.getClass());
System.out.println(myClass2.getClass());

//结果都是xxx.xxx.xxx.MyClass,两者一样的
  • 也就是说,逻辑上可以看成不同类型,但本质上、实际上还是”定义的类“类型

2.2 派生泛型类

2.2.1 子类定义

  • 子类是泛型类,则子类父类泛型要一致
class Child extends Father
class Child extends Father    
  • 子类不是泛型类,则父类要明确数据类型
class Child extends Father

2.3 泛型接口

2.3.1 泛型接口定义

interface 接口名称<<泛型标识1, 泛型标识2...>{
    泛型标识 方法名;
    ...
}

2.3.2 泛型接口使用

  • 实现类是泛型类,则接口和实现泛型要一致
  • 实现类不是泛型类,则接口要明确数据类型
  • 总结,和子类父类泛型定义规则一样

3 泛型方法

3.1 语法

//真正的泛型方法
修饰符  返回类型 方法名(参数列表){
    //...方法实现
}

class MyClass{
    
    private T t;
    
    //注意这种不是泛型方法
    public T getT(){
        return t;
    }
    //这种是泛型方法,支持参数是泛型
    public  int getSize(ArrayList arry){
        return arry.size();
    }
    
}


  • 修饰符是指public一类的
  • 只有声明了<>的方法才是泛型方法

3.2 使用

public class A{
    public Static  void print(E... e){//E... e可以看做数组参数E[] e
        for(int i = 0; i < e.lenth; i++)
            System.out.println(E[i]);
    }
}

A.print(1,2,3,4);
A.print("aaa","bbb");

4 类型通配符

4.1背景

  • List对象不能被当成List对象使用,也就是说:List类并不是List类的子类。即使Integer是Object类的子类。
    public static void test(List list){
        for(int i = 0 ; i < list.size(); i ++){
            System.out.println(list.get(i));
        }
    }
    
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(1);
        test(list);
    }
     
     

    4.2 概念

    • 类型通配符一般使用代替
    public static void test(List list)
    
    • 类型通配符是实参类型。所谓实参类型,就是具体的类型如Integer这样,但这里的?表示的实参类型能代表所有别的类型

    4.3 类型通配符上限

    4.3.1 语法

    类/接口 
    

    4.3.2 概念

    • 表示能传进去实参类型是类型上限,即只能传该类型或该类型的子类

    4.3.3 例子

    public static void test(List list){
        //...
    }
    
    pubulic static void main(String[] args) {
        List list1 = new ArrayList();
        List list2 = new ArrayList();
        List list3 = new ArrayList();
        list1.add(1);
        list2.add(1);
        list3.add("aaa");
        test(list1);//可以
        test(list2);//可以
        test(list3);//不行
    }
    

    4.4 类型通配符下限

    4.4.1 语法

    类/接口 
    

    4.4.2 概念

    • 表示能传进去实参类型是类型下限,即只能传该类型或该类型的父类

    4.4.3 概念

    public static void test(List list){
        //...
    }
    
    pubulic static void main(String[] args) {
        List list1 = new ArrayList();
        List list2 = new ArrayList();
        List list3 = new ArrayList();
        List list4 = new ArrayList();
        list1.add(1);
        list2.add(1);
        list3.add("aaa");
        list4.add("aaa");
        test(list1);//不行
        test(list2);//可以
        test(list3);//不行
        test(list4);//可以
    }
     
     

    5 类型擦除

    5.1 概念

    • Java 1.5以后引入的概念,即引入泛型的时候就有了。
    • 泛型信息只存在于代码编译阶段,在进去JVM之前,泛型信息将被擦除,称之为类型擦除

    5.2 举例

    5.2.1 基本泛型类

    List list1 = new ArrayList();
    List list2 = new ArrayList();
    List list3 = new ArrayList();
    List list4 = new ArrayList();
    
    System.out.println(list1.getClass().getSimpleName());
    System.out.println(list2.getClass().getSimpleName());
    System.out.println(list3.getClass().getSimpleName());
    System.out.println(list4.getClass().getSimpleName());
    
    //结果都是一样,类型被擦除了
    ArrayList
    ArrayList
    ArrayList
    ArrayList
     
     

    5.3 类型擦除种类

    5.3.1 无限制类型擦除

    generic1.png

    5.3.2 有限制类型擦除

    generic2.png

    5.3.3 方法中类型擦除

    generic3.png

    5.3.4 桥接方法的类型擦除

    generic4.png
    • 首先,要保证原有接口的类型擦除。
    • 其次,接口实现类中,首先要进行类型擦除
    • 最后,桥接方法的类型擦除,是保证实现类重写了接口的方法(override)

    6 泛型数组

    6.1 泛型数组的创建

    • 可以声明带泛型的数组引用,但不能直接创建泛型数组对象
      • 原因:泛型在编译期做类型擦除,而数组会在整个编译期都保持类型,二者设计上就有冲突
    ArrayList[] listArray = new ArrayList<>[5];         //直接创建,不行
    ArrayList[] listArray;                              //声明引用,可以
    
    ArrayList[] list = new ArrayList[5];                        //创建一个ArrayList类型的数组
    ArrayList[] listArray = list;                       //引用,可以,但是有问题,上面的类型可能出问题,不一定保证是String,容易出现类型异常
    
    ArrayList[] listArray = new ArrayList[5];           //这种才是创建泛型数组的方法
    
    • 可以通过java.lang.reflect.Array的newInstance(Class, int )方法创建T数组(需要强制转换)
    ArrayList[] listArray2 = (ArrayList[]) Array.newInstance(String.class, 2 );
    

    7 泛型与反射

    7.1 反射常用泛型类

    • Class获取类的类型对象
    • Constructor获取类的构造器对象

    参考

    • https://www.bilibili.com/video/BV1xJ411n77R?p=12

    你可能感兴趣的:(Java泛型笔记)