Java中的泛型(Generics)

152 阅读3分钟

什么是泛型? 在Java中,泛型是一种在编译时检查类型安全性的机制。它允许程序员编写方法或类,其中的数据类型可以在运行时确定。这使得代码更加灵活和可重用。 泛型的基本用法 泛型的基本用法包括定义泛型类和泛型方法。 定义泛型类

public class Box<T> {
    private T item;

    public Box(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }

    public void setItem(T item) {
        this.item = item;
    }
}

public class Main {
    public static void main(String[] args) {
        Box<String> stringBox = new Box<>("Hello");
        String s = stringBox.getItem(); // 编译器自动类型检查
    }
}

在这个例子中,Box 类使用了一个类型参数 T,表示它可以存储任何类型的对象。当创建 Box 对象时,必须指定具体的类型,例如 Box 表示存储字符串的对象。

    public class Utility {
    public static <T> void swap(T[] array, int i, int j) {
        T temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    public static void main(String[] args) {
        Integer[] numbers = {1, 2, 3};
        swap(numbers, 0, 2); // 编译器自动类型检查
        System.out.println(Arrays.toString(numbers)); // 输出 [3, 2, 1]
    }
}
在这个例子中,swap 方法使用了一个类型参数 T,表示它可以交换任何类型的数组元素。当调用 swap 方法时,编译器会自动推断类型参数 T

泛型的限制 有时候我们需要对泛型添加一些限制,以便更好地利用类型特性。 泛型的上界 使用 extends 关键字可以指定泛型的上界。

    public class UpperBound<T extends Number> {
    private T item;

    public UpperBound(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }

    public void setItem(T item) {
        this.item = item;
    }
}

public class Main {
    public static void main(String[] args) {
        UpperBound<Integer> integerBox = new UpperBound<>(10);
        // UpperBound<String> stringBox = new UpperBound<>("Hello"); // 错误,String 不是 Number 的子类
    }
}

在这个例子中,UpperBound 类的类型参数 T 必须是 Number 的子类。 泛型的下界 使用 super 关键字可以指定泛型的下界。

    public class LowerBound<T super Number> {
    private T item;

    public LowerBound(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }

    public void setItem(T item) {
        this.item = item;
    }
}

public class Main {
    public static void main(String[] args) {
        LowerBound<Number> numberBox = new LowerBound<>(new Number() {});
        // LowerBound<String> stringBox = new LowerBound<>("Hello"); // 错误,String 不是 Number 的超类
    }
}

在这个例子中,LowerBound 类的类型参数 T 必须是 Number 的超类。 泛型擦除 Java中的泛型是编译时的概念,在运行时会被擦除(erasure)。这意味着在运行时,所有的泛型类型都会被替换为其对应的原始类型。

    public class GenericExample<T> {
    private T item;

    public T getItem() {
        return item;
    }

    public void setItem(T item) {
        this.item = item;
    }
}

public class Main {
    public static void main(String[] args) {
        GenericExample<String> stringBox = new GenericExample<>();
        stringBox.setItem("Hello");
        String s = stringBox.getItem(); // 编译器自动类型检查

        // 在运行时,stringBox 实际上是一个 GenericExample<Object>
        Object obj = stringBox.getItem(); // 运行时类型为 Object
    }
}

尽管编译器能够进行类型检查,但在运行时泛型类型会被擦除,因此只能访问其原始类型。 总结 Java中的泛型提供了一种强大的类型安全机制,使代码更加灵活和可重用。通过定义泛型类和泛型方法,可以轻松地处理不同类型的数据。同时,通过添加泛型的上下界限制,可以进一步增强类型的安全性和灵活性。理解泛型擦除的概念有助于更好地理解Java中的类型系统和内存管理。