Java 中,泛型(Generics) 是一种提供类型安全代码复用的机制。它允许你在类、接口和方法中定义类型参数,从而编写更加通用和灵活的代码。


1. 为什么需要泛型?

在 Java 5 之前,集合类(如 ArrayList)只能存储 Object 类型,这导致了以下问题:

  • 类型不安全:需要强制类型转换,容易出现 ClassCastException
  • 代码不清晰:无法明确知道集合中存储的具体数据类型。

没有泛型的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.ArrayList;

public class Main {
public static void main(String[] args) {
ArrayList list = new ArrayList(); // 使用Object存储
list.add("Hello");
list.add(123); // 不同类型的数据

// 需要强制类型转换
String str = (String) list.get(0);
System.out.println(str);

// 类型转换错误会抛出异常
String num = (String) list.get(1); // ClassCastException
}
}

解决方法:使用泛型

1
2
3
4
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
// list.add(123); // 编译时报错,避免类型错误
String str = list.get(0); // 不需要强制类型转换

2. 泛型的基本语法

定义泛型类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Box<T> {
private T content;

public void setContent(T content) {
this.content = content;
}

public T getContent() {
return content;
}
}

public class Main {
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.setContent("泛型示例");
System.out.println("内容: " + stringBox.getContent());
}
}

解释:

  • <T>:表示泛型类型,T 是一个占位符,可以用任意字母代替。
  • Box<String>:指定泛型类型为 String,确保类型安全。

3. 泛型方法

泛型不仅可以用于类,还可以用于方法。

泛型方法示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Main {

// 定义一个泛型方法
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}

public static void main(String[] args) {
Integer[] intArray = {1, 2, 3};
String[] strArray = {"Java", "Python", "C++"};

printArray(intArray);
printArray(strArray);
}
}

解释:

  • <T>:在方法返回类型之前声明。
  • T[] array:泛型数组参数。
  • 方法可以接受任意类型的数组。

4. 泛型的类型边界

有时候我们需要对泛型的类型进行限制,Java 提供了两种方式:

(1) 上界通配符: <? extends T>

  • 适用于读取数据。
  • 表示类型必须是 TT 的子类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.*;

public class Main {
public static void printNumbers(List<? extends Number> list) {
for (Number num : list) {
System.out.println(num);
}
}

public static void main(String[] args) {
List<Integer> intList = Arrays.asList(1, 2, 3);
List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);

printNumbers(intList);
printNumbers(doubleList);
}
}

(2) 下界通配符: <? super T>

  • 适用于写入数据。
  • 表示类型必须是 TT 的父类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.*;

public class Main {
public static void addNumbers(List<? super Integer> list) {
list.add(100);
list.add(200);
System.out.println(list);
}

public static void main(String[] args) {
List<Number> numberList = new ArrayList<>();
addNumbers(numberList);
}
}

5. 泛型中的通配符 <?>

  • <?> 表示未知类型,称为通配符
  • 常用于方法参数中,表示可以接受任何类型的集合。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.*;

public class Main {
public static void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}

public static void main(String[] args) {
List<String> strList = Arrays.asList("Java", "Python");
List<Integer> intList = Arrays.asList(1, 2, 3);

printList(strList);
printList(intList);
}
}

6. 泛型的限制

  1. 基本数据类型不能作为泛型类型

    1
    List<int> list = new ArrayList<>(); // 错误

    解决方法: 使用包装类 Integer 替代 int

    1
    List<Integer> list = new ArrayList<>();
  2. 不能创建泛型数组

    1
    T[] array = new T[10]; // 错误

    解决方法: 使用 Object 数组并强制转换:

    1
    2
    @SuppressWarnings("unchecked")
    T[] array = (T[]) new Object[10];

总结

  • 泛型类:使用 <T> 声明通用类型,使类可以操作不同的数据类型。
  • 泛型方法:在方法中使用 <T> 使其支持不同类型的参数。
  • 通配符 <?>:表示未知类型,用于通用代码。
  • 上界 <? extends T>:用于读取数据。
  • 下界 <? super T>:用于写入数据。