教程中心 / Java教程 / 泛型入门

泛型入门

10分钟 泛型

什么是泛型?

泛型(Generic)是Java 5引入的一个重要特性,它的本质是参数化类型。简单来说,泛型就是给类型写参数,就像方法接收参数一样。泛型让你编写更通用、更安全的代码。

生活比喻

想象你要寄送包裹。没有泛型之前,你可能需要一个"字符串包裹盒"专门装字符串,一个"整数包裹盒"专门装整数...这太浪费了!泛型就像一个万能包裹盒,你告诉它你要装什么类型,它就能完美适配——既安全又高效。

没有泛型的问题

在Java 5之前,集合(如ArrayList)只能存储Object类型。这意味着你可以把任何对象放进去,但取出来时需要强制类型转换,这会带来两个严重问题:

问题1:类型安全问题

编译时不会报错,运行时才知道类型错误。想象你在处理用户数据时突然收到一个完全意想不到的类型...

// 没有泛型的时代
List list = new ArrayList();
list.add("Hello");
list.add(123);  // 编译器不会报错

// 取出来时问题来了
String s = (String) list.get(1);  // 运行时ClassCastException!
System.out.println(s.length());   // 这行根本不会执行到

问题2:代码繁琐

每次从集合取元素都需要强制类型转换,代码又臭又长。

使用泛型的优势

类型安全

编译时就能发现类型错误,而不是等到运行时才崩溃

消除强制类型转换

代码更简洁,编译器自动插入类型转换代码

代码复用

一套泛型代码可以处理多种类型

泛型基本语法

1. 泛型类

// 定义泛型类 - T是类型参数
class Box {
    private T value;

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

    public T get() {
        return value;
    }
}

// 使用时指定具体类型
Box stringBox = new Box<>();
stringBox.set("Hello");
String s = stringBox.get();  // 不需要强制转换!

Box intBox = new Box<>();
intBox.set(123);
Integer num = intBox.get();  // 类型安全!

2. 泛型接口

// 定义泛型接口
interface Container {
    void add(E element);
    E get(int index);
    int size();
}

// 实现泛型接口
class StringContainer implements Container {
    private String[] array = new String[10];
    private int size = 0;

    @Override
    public void add(String element) {
        array[size++] = element;
    }

    @Override
    public String get(int index) {
        return array[index];
    }

    @Override
    public int size() {
        return size;
    }
}

3. 泛型方法

// 泛型方法 - 类型参数在返回值前面
public static  T getFirst(T[] array) {
    if (array == null || array.length == 0) {
        return null;
    }
    return array[0];
}

// 使用
String[] names = {"Alice", "Bob"};
Integer[] nums = {1, 2, 3};

String first = getFirst(names);   // 返回 String
Integer firstNum = getFirst(nums); // 返回 Integer

类型参数的命名约定

Java泛型有几种常见的类型参数命名,虽然你可以用任何标识符,但遵循约定让你的代码更易读:

字母含义使用场景
EElement集合中的元素
KKeyMap的键
VValueMap的值
TType通用类型
NNumber数字类型
RResult返回类型
S, U, V第二、第三、第四类型多类型参数

多类型参数

泛型可以定义多个类型参数,就像Map有K(键)和V(值):

class Pair {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() { return key; }
    public V getValue() { return value; }
}

// 使用
Pair entry = new Pair<>("年龄", 25);
System.out.println(entry.getKey() + ": " + entry.getValue());

泛型在哪里使用?

Java标准库中有大量使用泛型的例子,你每天都在使用:

1

集合框架

List<String> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
Set<Integer> set = new HashSet<>();
2

Stream API

List<String> names = people.stream()
    .map(Person::getName)
    .collect(Collectors.toList());
3

Optional

Optional<String> optional = Optional.of("value");
String value = optional.orElse("default");

泛型的局限

重要事实:泛型只在编译时有效

泛型信息只存在于编译阶段,运行时会被"擦除"(这叫做类型擦除,我们后面会详细讲解)。所以你不能通过反射在运行时获取泛型的类型参数。

// 这个操作在编译时会报错
List stringList = new ArrayList<>();
List intList = new ArrayList<>();

// 虽然stringList和intList内容不同,但它们运行时类型都是ArrayList
System.out.println(stringList.getClass() == intList.getClass());  // true!

小结

  • 泛型是Java 5引入的参数化类型特性,让代码更通用、更安全
  • 泛型提供了编译时类型安全,避免ClassCastException
  • 使用<T>语法定义类型参数
  • 泛型消除强制类型转换,让代码更简洁
  • 泛型支持类、接口、方法的定义
  • 遵循命名约定:E、K、V、T等
☕ Java 在线代码编辑器
📝 运行结果