Java 泛型完整知识点笔记

Java 泛型完整知识点笔记

一、泛型定义 & 基本使用

泛型从 JDK 1.5 开始引入:

  1. 在编译阶段完成类型校验,类型错误编译时期就报错;
  2. 避免频繁强制类型转换;
  3. 只作用于编译阶段。

无泛型(存在类型转换异常风险)

@Test
public void testGeneric() {
    ArrayList list = new ArrayList();
    list.add("China");
    list.add(1);   // 可以存入任意类型,无法限制

    // 取出必须强转,容易发生 ClassCastException
    String str = (String) list.get(0);
}

使用泛型(类型约束 + 无需强转)

@Test
public void testGeneric2() {
    // 指定集合元素只能是 String
    ArrayList<String> list = new ArrayList<String>();
    list.add("China");
    // list.add(1); // 编译报错,提前拦截错误

    // 取出直接就是 String,不用强制转换
    String str = list.get(0);
}

泛型书写规则

@Test
public void test3() {
    // 规范写法
    List<Object> list1 = new ArrayList<Object>();
    // JDK7 菱形语法:右侧泛型可以省略
    List<String> list3 = new ArrayList<>();

    // 只右侧写泛型无效,整体还是原生集合
    List list4 = new ArrayList<String>();
    list4.add(1);

    // 左右泛型类型不一致,编译报错
    // List<Object> list5 = new ArrayList<String>();

    // 泛型只能写引用类型,基本数据类型不行
    // List<int> list6 = new ArrayList<>();
}

泛型擦除

  1. 泛型只在编译期生效,编译生成 class 字节码后,泛型标记会被擦除;
  2. 目的:保证 JDK 向下兼容老版本无泛型代码。

验证泛型擦除

List<String> list1 = new ArrayList<String>();
List<Integer> list2 = new ArrayList<Integer>();
Class c1 = list1.getClass();
Class c2 = list2.getClass();
// 运行时两者都是 ArrayList.class,输出 true
System.out.println(c1 == c2);

运行期间不存在 ArrayList<String>、ArrayList<Integer> 的区分,统一为 ArrayList。

泛型方法 / 泛型类 / 泛型接口

作用:抽取通用代码,制作通用工具、通用DAO,提升代码复用性与灵活性。

泛型方法

在方法上声明泛型<K,T>,调用方法时自动确定泛型类型。

public class GenericDemo2 {
    // 普通重载方法,扩展性差
    public Student add1(Student student, Teacher teacher) {
        return null;
    }

    // 泛型方法:<K,T> 声明泛型
    public <K,T> K add(K k, T t) {
        return k;
    }

    @Test
    public void test1() {
        // 调用时自动推导类型
        Float result1 = add(1.0f, 1);
        System.out.println(result1);

        String result2 = add("abc", 1);
        System.out.println(result2);
    }
}

泛型类

在类名上定义泛型,整个类内部均可使用该类型,不用每个方法单独声明。

// 通用DAO模板
public class BaseDao <T>{
    private List<T> list = new ArrayList<>();

    public void add(T t) {}
    public void update(T t) {}
    public T selectById(Integer id) {
        return null;
    }
}

@Test
public void test2() {
    // 操作Student
    BaseDao<Student> baseDao1 = new BaseDao<>();
    baseDao1.add(new Student());

    // 复用同一个类,操作Teacher
    BaseDao<Teacher> baseDao2 = new BaseDao<>();
    baseDao2.add(new Teacher());
}

泛型接口

定义泛型接口

public interface IBaseDao<T> {
    void add(T t );
    void update(T t );
}

实现类确定泛型类型

// 实现接口时直接指定泛型为 Person
public class PersonDao implements IBaseDao<Person> {
}

// 指定泛型为 Student
public class StudentDao implements IBaseDao<Student> {
}

泛型通配符与限定符

泛型字母命名约定

只是命名习惯,任意大写字母均可:

标识 含义
T(Type) 任意实体类型
E(Element) 集合中的元素
K / V 键值 Key / Value
N(Number) 数值类型
? 未知类型、通配符

? 无界通配符

List<?> 代表任意类型的 List,可以接收任意泛型集合,只能读取、不能新增元素,保证安全。

public void add(List<?> list) {
    // list.add(xxx); 无法添加元素
}

? extends 上限限定(上边界)

? extends Number:集合元素只能是 Number 及其子类(Integer、Double、Float等)。 只能读取,不能写入。

// 元素只能是 Number 的子类
public void add(List<? extends Number> list) {
}

@Test
public void testGeneric23() {
    List<Double> list1 = new ArrayList<>();
    List<Integer> list3 = new ArrayList<>();
    add(list1);
    add(list3);

    // List<String> 不属于Number子类,编译报错
    // List<String> list4 = new ArrayList<>();
    // add(list4);
}

? super 下限限定(下边界)

? super String:集合元素只能是 String 本身或 String 的父类。 可以写入元素。

// 元素只能是String 或 String的父类
public void add(List<? super String> list) {
}

@Test
public void testGeneric24() {
    List<Object> list1 = new ArrayList<>();
    List<String> list2 = new ArrayList<>();
    add(list1);
    add(list2);

    // Integer不是String父类,报错
    // List<Integer> list3 = new ArrayList<>();
    // add(list3);
}

上下限总结

1. <? extends T>:上限,可读不可写;
2. <? super T>:下限,可写可读;
3. <?>:无界,可读不可写。

项目中常用泛型实体

统一返回结果类 Result

后端统一返回格式,data 泛型适配任意返回数据:

public class Result<T> {
    public static final int ERROR = 1;
    public static final int OK = 0;

    private Integer code;   // 状态码 0成功 1失败
    private String msg;     // 提示信息
    private T data;         // 返回的数据,泛型适配任意类型
}

分页实体 PageInfo

分页通用模板,泛型适配不同实体分页:

public class PageInfo<T> {
    private Integer pageNo;
    private Integer pageSize;
    private List<T> list;
    private Integer totalPage;
}

核心总结

  1. 泛型作用:编译期类型检查、免去强制类型转换;
  2. 编译后发生泛型擦除,运行不存在泛型;
  3. 泛型类/接口用于抽取通用模板(DAO、分页、统一返回体);
  4. extends 限定上限、super 限定下限,用来控制集合元素类型范围。
上一篇
下一篇