跳转到内容

泛型机制

来自代码酷

泛型机制[编辑 | 编辑源代码]

泛型(Generics)是Java编程语言中一项强大的特性,它允许开发者在编译时检测类型错误,提高代码的安全性和可重用性。泛型通过参数化类型(type parameters)来实现,使得类、接口和方法可以操作多种数据类型而不必牺牲类型安全性。

介绍[编辑 | 编辑源代码]

泛型的主要目的是在编译时提供更强的类型检查,并消除类型转换的需要。在Java 5之前,集合类(如ArrayList)只能存储Object类型,这意味着每次从集合中取出元素时都需要进行强制类型转换,容易引发运行时错误。泛型的引入解决了这一问题。

泛型的核心概念包括:

  • 类型参数:在类、接口或方法声明中使用的占位符,例如`<T>`。
  • 类型擦除:Java泛型在运行时会被擦除,转换为原始类型(raw types)。
  • 通配符:`?`用于表示未知类型,通常与`extends`和`super`结合使用。

基本语法[编辑 | 编辑源代码]

泛型类[编辑 | 编辑源代码]

泛型类在类名后使用尖括号声明类型参数。例如:

public class Box<T> {
    private T content;

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

    public T getContent() {
        return content;
    }
}

使用示例:

Box<String> stringBox = new Box<>();
stringBox.setContent("Hello, Generics!");
String value = stringBox.getContent(); // 无需强制类型转换
System.out.println(value); // 输出: Hello, Generics!

泛型方法[编辑 | 编辑源代码]

泛型方法在返回类型前声明类型参数:

public <T> void printArray(T[] array) {
    for (T element : array) {
        System.out.println(element);
    }
}

使用示例:

Integer[] intArray = {1, 2, 3};
printArray(intArray); // 输出: 1 2 3

类型擦除[编辑 | 编辑源代码]

Java的泛型是通过类型擦除实现的,这意味着泛型类型信息在运行时不可用。例如,`Box<String>`和`Box<Integer>`在运行时都会被擦除为`Box`。

擦除后的代码示例[编辑 | 编辑源代码]

编译前:

Box<String> box = new Box<>();
box.setContent("Test");
String content = box.getContent();

编译后(等效代码):

Box box = new Box();
box.setContent("Test");
String content = (String) box.getContent();

通配符[编辑 | 编辑源代码]

通配符`?`用于表示未知类型,通常用于方法参数或变量声明。

上限通配符(extends)[编辑 | 编辑源代码]

限制类型为某个类的子类:

public void process(List<? extends Number> numbers) {
    for (Number num : numbers) {
        System.out.println(num);
    }
}

下限通配符(super)[编辑 | 编辑源代码]

限制类型为某个类的父类:

public void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
}

实际应用案例[编辑 | 编辑源代码]

集合框架中的泛型[编辑 | 编辑源代码]

Java集合框架广泛使用泛型来保证类型安全:

List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
// names.add(123); // 编译错误,类型不匹配

自定义比较器[编辑 | 编辑源代码]

泛型可用于创建可重用的比较逻辑:

public class Comparator<T extends Comparable<T>> {
    public T max(T a, T b) {
        return a.compareTo(b) > 0 ? a : b;
    }
}

高级主题[编辑 | 编辑源代码]

类型边界[编辑 | 编辑源代码]

可以使用`extends`限制类型参数的范围:

public class Calculator<T extends Number> {
    public double add(T a, T b) {
        return a.doubleValue() + b.doubleValue();
    }
}

泛型与继承[编辑 | 编辑源代码]

泛型类可以继承或实现其他泛型类/接口:

public interface Repository<T> {
    void save(T entity);
}

public class UserRepository implements Repository<User> {
    @Override
    public void save(User user) {
        // 实现保存逻辑
    }
}

常见问题[编辑 | 编辑源代码]

为什么不能创建泛型数组?[编辑 | 编辑源代码]

由于类型擦除,Java不允许创建泛型数组。例如:

// 以下代码会导致编译错误
List<String>[] arrayOfLists = new List<String>[10];

泛型与原始类型的区别[编辑 | 编辑源代码]

原始类型(raw types)是不带类型参数的泛型类,为了向后兼容而保留,但不推荐使用:

List rawList = new ArrayList(); // 原始类型
rawList.add("String");
rawList.add(1); // 编译通过,但可能引发运行时错误

总结[编辑 | 编辑源代码]

泛型是Java中提高代码安全性和可重用性的重要特性。通过类型参数化,开发者可以:

  • 在编译时捕获类型错误
  • 消除强制类型转换
  • 编写更通用的代码

虽然类型擦除带来了一些限制,但合理使用泛型能显著提高代码质量。对于初学者,建议从简单的泛型类和泛型方法开始练习,逐步掌握更高级的用法如通配符和类型边界。