一、泛型的概念

泛型就是参数化类型,把数据类型当作参数传递,把类型当作参数传进去,用什么类型,就指定什么类型,代码更通用、更安全

泛型 = 万能盒子

  • 你买一个盒子,不规定它装什么。
  • 用的时候再说:装苹果、装书、装玩具。
  • 泛型就是:先不写死类型,用的时候再指定

二、泛型的语法

1. 泛型类 / 接口

class 泛型类名称<类型形参列表> {
    // 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> {  
}

< T > 就是泛型参数,表示任意类型
T 可以是任意字母(E、K、V、T 都可以)

在这里插入图片描述

2. 泛型方法

public <T> T 方法名(T t) {
    return t;
}

三、泛型类的使用

示例:

// 泛型类:T 代表传入的类型
class Box<T> {
    private T data;

    public void set(T data) {
        this.data = data;
    }

    public T get() {
        return data;
    }
}

使用:

public class Test {
    public static void main(String[] args) {
        // 存字符串
        Box<String> box1 = new Box<>();
        box1.set("Hello");
        String s = box1.get();

        // 存数字
        Box<Integer> box2 = new Box<>();
        box2.set(100);
        int i = box2.get();
    }
}

注意

  1. 不能new泛型类型的数组,比如说:
T[] ts = new T[5];
  1. 泛型只能接受类,所有的基本数据类型必须使用包装类

四、泛型如何编译的(面试常考)

1. 擦除机制

通过命令:javap -c 查看Box.class 字节码文件,所有的T都是Object
在这里插入图片描述
在编译的过程当中,将所有的T替换为Object这种机制,我们称为:擦除机制
Java的泛型机制是在编译级别实现的。编译器生成的字节码在运行期间并不包含泛型的类型信息,也就是说编译完成后,生成 .class 文件时,会把 全部删掉
总结一下:
当你使用泛型时,编辑器做了三件事

  1. 编译期间:类型检查
  • 比如你写了ArrayList < String >,编译器就只允许你 add String,放错类型直接报错!
  1. 编译后:擦除泛型
  • < String > 直接删掉,变回原始类型(Object)
  1. 自动帮你强转
  • 取数据时,编译器偷偷帮你加 (String)

五、泛型的上界

在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束,限制传入的类型,只能是 某个类它的子类

语法:

class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参数 */ {
    // 这里可以使用类型参数
}

示例:

public static void show(Box<E extends Number> box) { }

上界 Number

  • 能传:Integer、Double、Short……(都是 Number 的子类)
  • 不能传:String、Student……(不是子类)

复杂示例:

public class MyArray<E extends Comparable<E>> {
    ...
}

E必须是实现了Comparable接口的

Logo

汇聚全球AI编程工具,助力开发者即刻编程。