泛型

泛型

泛型的定义

Java泛型是在Java SE 5中引入的一种特性,它允许你为你的类、接口和方法指定类型参数,从而使得代码更加类型安全和灵活。泛型的本质是参数化类型,即在编译时提供类型信息,以确保类型的正确性。

java复制public class Box<T> {
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

泛型的作用

  • 类型安全:泛型提供了编译时类型检查,减少了运行时类型错误。
  • 消除类型转换:使用泛型可以减少类型转换,使代码更简洁。
  • 代码复用:泛型允许你编写更通用的类和方法,可以用于不同的数据类型。

泛型的特性

Java泛型是Java 5 引入的一个特性,它为Java语言带来了类型安全和代码复用性。以下是Java泛型的主要特性:

  • 类型安全
    泛型的主要目的是提供类型安全。使用泛型,你可以在编译时检查类型错误,避免在运行时出现类型转换异常。

  • 消除类型转换
    泛型消除了在集合中存储和检索对象时需要进行的类型转换。这使得代码更简洁,减少了出错的可能性。

  • 代码复用
    泛型允许你编写可以用于多种类型的通用类和方法,从而提高代码复用率。

  • 类型参数化
    泛型允许你为类、接口、方法定义类型参数,这些参数在实例化或调用时指定。

  • 类型推断
    Java编译器能够推断泛型的类型,这减少了代码的冗余,使得代码更加简洁。

  • 泛型通配符
    使用问号?可以定义一个通配符类型,它可以接受任何类型的参数。

  • 类型上限
    使用extends关键字,可以为泛型指定一个类型上限,即泛型参数必须是上限类型或其子类型。

  • 类型下限(不常用):
    使用super关键字,可以为泛型指定一个类型下限,但这种做法在Java中不常用。

  • 协变与逆变
    泛型是协变的,意味着如果ST的子类型,则List<S>List<T>的子类型。逆变通常用于方法参数,但Java泛型不支持泛型的逆变。

  • 类型擦除
    泛型的类型信息在编译时存在,但在运行时被擦除,这意味着运行时无法直接获取泛型的类型信息。

  • 泛型数组限制
    由于类型擦除,Java不允许创建泛型类型的数组。

  • 非重新实例化
    泛型类和接口不能被重新实例化,即不能使用泛型参数创建它们的子类。

  • 与现有代码的兼容性
    泛型被设计为与现有非泛型代码兼容,这允许逐步将泛型集成到现有代码库中。

  • 限制与原始类型
    当泛型类型使用未指定实际类型时,它被称为原始类型(如List而不是List<String>)。使用原始类型会失去泛型的类型安全。

  • 桥方法
    当泛型类继承非泛型类时,编译器会生成桥方法来保持二进制兼容性。

泛型的使用

定义泛型类

public class Box<T> {
    private T item;

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

    public T getItem() {
        return item;
    }
}

使用泛型类

Box<String> stringBox = new Box<>();
stringBox.setItem("Hello, World!");
String item = stringBox.getItem();

泛型接口

public interface Stack<T> {
    void push(T item);
    T pop();
}

泛型方法

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

使用泛型方法

String[] strings = {"a", "b", "c"};
Utils.printArray(strings);
Integer[] numbers = {1, 2, 3};
Utils.printArray(numbers);

泛型通配符

public class WildcardTest {
    public static void printArrayWithBounds(List<? extends Number> list) {
        for (Number n : list) {
            System.out.println(n);
        }
    }

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

泛型的类型限定

public class GenericClass<T extends Comparable<T>> {
    // T 必须实现了 Comparable 接口
    public void compare(T a, T b) {
        a.compareTo(b);
    }
}

泛型方法的类型限定

public class MathUtils {
    public static <T extends Number> double calculate(T a, T b, MathOperation<T> operation) {
        return operation.performOperation(a, b);
    }
}

interface MathOperation<T extends Number> {
    double performOperation(T a, T b);
}

泛型的类型擦除

Java泛型的类型信息在编译时被检查,但在运行时会被擦除,这意味着你不能直接获取泛型的类型信息:

Box<String> box = new Box<>();
System.out.println(box.getItem().getClass()); // 正确使用
System.out.println(box.getClass().getTypeParameters()); // 编译错误,因为类型信息已被擦除

泛型数组的限制

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

List<String>[] lists = new List<String>[10]; // 编译错误
List<String>[] lists = new ArrayList<String>[10]; // 正确

使用原始类型

有时,为了与旧代码兼容,你可能需要使用原始类型:

List list = new ArrayList(); // 使用原始类型
list.add("String"); // OK
list.add(1); // 编译时不报错,但运行时会抛出 ClassCastException

使用泛型时,应该尽量避免使用原始类型,以保持类型安全。

通过这些用法,你可以创建类型安全、灵活且可重用的代码。Java泛型是Java语言中一个非常重要的特性,广泛应用于集合框架、自定义数据结构和算法实现中。

posted @ 2024-08-07 19:45  wen-210162704027  阅读(1)  评论(0编辑  收藏  举报