Java 泛型

Java 泛型允许我们创建可用于不同数据类型的单个类、接口和方法。在本教程中,我们将通过示例了解 Java 泛型、如何创建泛型类和泛型方法以及泛型的优点。

在本教程中,我们将通过示例了解 Java 泛型、如何创建泛型类和泛型方法以及泛型的优点。

Java 泛型允许我们创建可用于不同数据类型的单个类、接口和方法。这有助于我们重用我们的代码。

不能将原始数据类型用作泛型类型。

Java 泛型类

我们可以创建一个可以用于任何类型数据的类,这样的类称为泛型类。

下面是我们如何在 Java 中创建泛型类:

示例:创建泛型类

// 定义一个泛型类
class GenericsClass<T> {
  // 属性 data 的类型是不确定的,使用 T 表示
  private T data;

  public GenericsClass(T data) {
    this.data = data;
  }

  // 返回类型也是泛型
  public T getData() {
    return this.data;
  }
}

public class Main {
  public static void main(String[] args) {
    // 使用整数初始化泛型类
    GenericsClass<Integer> intObj = new GenericsClass<>(5);
    System.out.println("泛型方法返回: " + intObj.getData());

    // 使用字符串初始化泛型类
    GenericsClass<String> stringObj = new GenericsClass<>("Java Programming");
    System.out.println("泛型方法返回: " + stringObj.getData());
  }
}

输出

泛型方法返回: 5
泛型方法返回: Java Programming

在上面的例子中,我们创建了一个名为 GenericsClass<T> 的泛型类, 此类可用于处理任何类型的数据。

public class GenericsClass<T> {...}

这里, 尖括号 <> 内使用 T 表示类型参数。在 Main 类内部,我们创建了两个泛型类的对象:

  • intObj - 这里是类型参数为 Integer,此对象只处理整数数据。
  • stringObj - 这里是类型参数为 String,此对象只处理字符串数据。

Java 泛型方法

与泛型类类似,我们也可以创建一个可以用于任何类型数据的方法。这样的类被称为泛型方法。

下面是我们如何在 Java 中创建泛型类:

示例:创建泛型方法

class DemoClass {
  // 带泛型的方法
  public <T> void genericsMethod(T data) {
    System.out.println("调用了泛型方法:");
    System.out.println("参数是: " + data);
  }
}

public class Main {
  public static void main(String[] args) {
    // 初始化对象
    DemoClass demo = new DemoClass();

    // 调用方法的泛型参数为 字符串
    demo.<String>genericsMethod("Java Programming");

    // 调用方法的泛型参数为 整数
    demo.<Integer>genericsMethod(25);
  }
}

输出

调用了泛型方法:
参数是: Java Programming
调用了泛型方法:
参数是: 25

在上面的例子中,我们创建了一个名为 genericsMethod 的泛型方法。

public <T> void genericMethod(T data) {...}

在这里,类型参数 <T> 插入在 public 修饰符之后和返回类型 void 之前。

我们调用泛型方法的时候,可以将实际类型 <String><Integer> 写下方法名称前的括号内。

demo.<String>genericMethod("Java Programming");
demo.<Integer>genericMethod(25);

注意:调用泛型方法的时候,泛型方法的类型参数可以省略。例如,

demo.genericsMethod("Java Programming");

在这种情况下,编译器根据传递给方法的值自动匹配类型参数。

有界类型

通常,泛型的类型参数可以接受任何数据类型(原始类型除外)。

但是,如果我们只想对某些特定类型(比如只能数字)使用泛型,那么我们可以使用有界类型。

我们使用 extends 关键字界定泛型类型的边界。例如,

<T extends A>

这意味着泛型类型 T 只能接受 A 的子类型的数据。

示例:有界类型

class GenericsClass<T extends Number> {
  public void display() {
    System.out.println("这是一个有界类型的泛型类。");
  }
}

public class Main {
  public static void main(String[] args) {
    // 错误
    GenericsClass<String> obj1 = new GenericsClass<>();
    // 正确
    GenericsClass<Integer> obj2 = new GenericsClass<>();
  }
}

在上面的例子中,我们创建了一个名为 GenericsClass 的类。注意下面的代码

<T extends Number>

这里, 泛型类的类型参数是界类型。这意味着泛型类只能处理 Number 的子数据类型 (IntegerDouble 等)。

而我们创建泛型类的对象时用的类型参数 String,如下:

GenericsClass<String> obj1 = new GenericsClass<>();

在这种情况下,我们将收到以下错误。

Main.java:11:19
java: 类型参数java.lang.String不在类型变量T的范围内
Main.java:11:51
java: 不兼容的类型: 无法推断GenericsClass<>的类型参数
    原因: 推论变量 T 具有不兼容的上限
      等式约束条件:java.lang.String
      下限:java.lang.Number

Java 泛型的优点

使用 Java 泛型会带来如下好处:

  1. 代码可重用性更高
  2. 在编译期间的类型检查就能发现类型错误

代码可重用性

借助 Java 中的泛型,我们可以编写可以处理不同类型数据的代码。例如,

public <T> void genericsMethod(T data) {...}

在这里,我们创建了一个泛型方法。同样的方法可用于对整数数据、字符串数据等执行操作。

编译时类型检查

泛型的类型参数提供了数据类型的信息。例如,

GenericsClass<Integer> list = new GenericsClass<>();

在这里,我们让 GenericsClass 仅处理 Integer 数据。如果我们尝试将其他数据类型的数据传递给此类,程序将在编译时生成错误。

与集合一起使用

集合框架广泛使用了泛型。例如,

// 创建一个存放 String 类型元素的 ArrayList
ArrayList<String> list1 = new ArrayList<>();

// 创建一个存放 Integer 类型元素的 ArrayList
ArrayList<Integer> list2 = new ArrayList<>();

在上面的例子中,我们使用了 ArrayList 类来处理不同类型的数据。

ArrayList 类似,其他集合(LinkedListQueueMap 等)也是基于泛型的。