java 泛型

泛型是 Java 5 引入 的语法,核心作用:编译期类型检查、避免强制类型转换、代码复用、类型安全

一、为什么需要泛型?(痛点)

没有泛型时,集合默认存 Object,取出必须强转,极易报 ClassCastException

java 复制代码
// 无泛型:不安全、需强转
ArrayList list = new ArrayList();
list.add("Java");
String str = (String) list.get(0); // 强制转换

泛型限定只能存指定类型,编译就报错,运行更安全。


二、基本语法

1. 泛型集合(最常用)

格式:容器<类型>

java 复制代码
// 只能存放 String,无需强转
ArrayList<String> list = new ArrayList<>(); // 菱形语法 <> Java7+
list.add("泛型");
String s = list.get(0); // 直接取值,不用强转

2. 泛型标记约定(行业习惯)

  • <T>:Type 任意类型(最常用)
  • <E>:Element 集合元素
  • <K>:Key 键
  • <V>:Value 值
  • <N>:Number 数字类型

三、1. 泛型类

类名后定义泛型,整个类可用该类型。

java 复制代码
// 定义泛型类
public class Box<T> {
    private T data;

    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
}

// 使用
Box<String> box1 = new Box<>();
box1.setData("字符串");

Box<Integer> box2 = new Box<>();
box2.setData(100);

四、2. 泛型方法

方法独立定义泛型 ,优先级高于类泛型,格式:<T> 返回值 方法名()

java 复制代码
public class GenericDemo {
    // 泛型方法
    public static <T> void print(T t) {
        System.out.println(t);
    }

    public static void main(String[] args) {
        print("Java");
        print(666);
        print(true);
    }
}

五、3. 泛型接口

接口上定义泛型,实现类必须指定类型或继续保留泛型。

java 复制代码
// 泛型接口
public interface IGeneric<T> {
    T getInfo();
}

// 方式1:实现类明确指定类型
class Impl1 implements IGeneric<String> {
    @Override
    public String getInfo() {
        return "指定字符串类型";
    }
}

// 方式2:实现类继续保留泛型
class Impl2<T> implements IGeneric<T> {
    @Override
    public T getInfo() {
        return null;
    }
}

六、通配符 ?(重点)

? 代表任意未知类型,多用于方法参数。

1. 无界通配符 <?>

匹配任意类型只能读、不能写(除了 null)。

java 复制代码
public static void show(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

2. 上界通配符 <? extends 父类>

限定:只能是该类 或 它的子类

规则:可读,不可写

java 复制代码
// 只接收 Number 及子类(Integer、Double、Long)
public static void test(List<? extends Number> list) {}

3. 下界通配符 <? super 子类>

限定:只能是该类 或 它的父类

规则:可写,可读只能用 Object

java 复制代码
// 只接收 Integer 及父类(Number、Object)
public static void test(List<? super Integer> list) {}

七、泛型边界 & 擦除(面试高频)

1. 泛型上限(限定类型范围)

java 复制代码
// T 只能是 Animal 或其子类
public class AnimalBox<T extends Animal> {}

2. 类型擦除(Java 泛型本质)

Java 泛型是伪泛型

  • 编译阶段生效,运行时泛型信息会被擦除
  • List<String>List<Integer> 运行时都是 List<Object>
  • 因此不能用泛型做重载判断、不能创建泛型数组、基本类型不能作泛型
java 复制代码
// 错误:编译认为两个方法签名一样,泛型被擦除
public void fun(List<String> list){}
public void fun(List<Integer> list){}

3. 泛型不支持基本类型

必须用包装类

List<Integer>

List<int>


八、泛型继承规则

List<String> 不是 List<Object> 的子类!

java 复制代码
List<String> strList = new ArrayList<>();
List<Object> objList = strList; // 编译报错

原因:防止类型污染。


九、总结速记

  1. 作用:类型安全、免强转、代码复用
  2. 三类用法:泛型类、泛型方法、泛型接口
  3. 通配符:
    • <?> 任意类型
    • <? extends T> 上界(子类)
    • <? super T> 下界(父类)
  4. 底层:类型擦除,运行无泛型信息
  5. 禁忌:不能用基本类型、不能基于泛型重载、慎用泛型数组