# Java 泛型

> 原文： [https://www.programiz.com/java-programming/generics](https://www.programiz.com/java-programming/generics)

#### 在本教程中，我们将通过示例了解 Java 泛型，如何创建泛型类和方法及其优势。

在 Java 中，**泛型**帮助创建可与不同类型的对象（数据）一起使用的类，接口和方法。 因此，允许我们重用我们的代码。

**注意**：**泛型**不适用于基本类型（`int`，`float`，`char`等）。

* * *

## 泛型的工作原理

要了解 Java 中如何使用**泛型**，我们可以使用 Java 集合框架的`ArrayList`类。

`ArrayList`类是泛型类的示例。 我们可以使用`ArrayList`存储任何类型的数据。 例如，

```java
import java.util.ArrayList;

class Main {
   public static void main(String[] args) {

      // create an array list to store Integer data
      ArrayList<Integer> list1 = new ArrayList<>();
      list1.add(4);
      list1.add(5);
      System.out.println("ArrayList of Integer: " + list1);

      // creates an array list to store String data
      ArrayList<String> list2 = new ArrayList<>();
      list2.add("Four");
      list2.add("Five");
      System.out.println("ArrayList of String: " + list2);

      // creates an array list to store Double data
      ArrayList<Double> list3 = new ArrayList<>();
      list3.add(4.5);
      list3.add(6.5);
      System.out.println("ArrayList of Double: " + list3);
   }
} 
```

输出量

```java
ArrayList of Integer: [4, 5]
ArrayList of String: [Four, Five]
ArrayList of Double:  [4.5, 6.5] 
```

在上面的示例中，我们使用了相同的`ArrayList`类来存储`Integer`，`String`和`Double`类型的元素。 由于 **Java 泛型**的缘故，这是可能的。

在这里，请注意这行，

```java
ArrayList<Integer> list1 = new ArrayList<>(); 
```

我们在尖括号`<>`中使用了`Integer`。 尖括号`<>`在泛型中称为**类型参数**。

类型参数用于指定泛型类或方法适用的对象（数据）的类型。

* * *

## 创建泛型类

现在我们知道了泛型在 Java 中的工作方式，让我们看看如何创建自己的泛型类。

### 示例：创建泛型类

```java
class Main {
  public static void main(String[] args) {

    // initialize generic class with Integer data
    GenericsClass<Integer> intObj = new GenericsClass<>(5);
    System.out.println("Generic Class returns: " + intObj.getData());

    // initialize generic class with String data
    GenericsClass<String> stringObj = new GenericsClass<>("Java Programming");
    System.out.println("Generic Class returns: " + stringObj.getData());
  }
}

class GenericsClass<T> {

  // variable of T type
  private T data;

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

  // method that return T type variable
  public T getData() {
    return this.data;
  }
} 
```

Output

```java
Generic Class returns: 5
Generic Class returns: Java Programing 
```

在上面的示例中，我们创建了一个名为`GenericsClass`的泛型类。 此类可用于处理任何类型的数据。

```java
class GenericsClass<T> {...} 
```

在此，`T`表示**类型参数**。 在`Main`类内部，我们创建了名为`intObj`和`stringObj`的`GenericsClass`对象。

*   在创建`intObj`时，类型参数`T`被`Integer`代替。 这意味着`intObj`使用`GenericsClass`处理整数数据。
*   在创建`stringObj`时，类型参数`T`被替换为`String`。 这意味着`stringObj`使用`GenericsClass`处理字符串数据。

* * *

## 创建泛型方法

与泛型类相似，我们也可以使用 Java 创建自己的泛型方法。

### 示例：创建泛型方法

```java
class Main {
  public static void main(String[] args) {

    // initialize the class with Integer data
    DemoClass demo = new DemoClass();
    demo.<String>genericsMethod("Java Programming");
  }
}

class DemoClass {

  // generics method
  public <T> void genericsMethod(T data) {
    System.out.println("This is a generics method.");
    System.out.println("The data passed to method is " + data);
  }
} 
```

**输出**

```java
This is a generics method.
The data passed to the method: Java Programming 
```

在上面的示例中，我们在普通类内部创建了一个名为`genericsMethod`的泛型方法。

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

在此，将类型参数`<T>`插入到修饰符（`public`）之后和返回类型（`void`）之前。

我们可以通过将实际类型`<String>`放在方法名称之前的方括号内来调用泛型方法。

```java
demo.<String>genericMethod("Java Programming"); 
```

**注意**：在大多数情况下，我们可以在调用泛型方法时省略类型参数。 这是因为编译器可以使用传递给方法的值来匹配类型参数。 例如，

```java
demo.genericsMethod("Java Programming"); 
```

* * *

## 有界类型

通常，**类型参数**可以接受任何数据类型（原始类型除外）。 但是，如果我们只想将泛型用于某些特定类型（例如接受数字类型的数据），则可以使用有界类型。

我们使用`extends`关键字。 例如，

```java
<T extends A> 
```

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

### 示例：有界类型

```java
class GenericsClass <T extends Number> {

  public void display() {
    System.out.println("This is a bounded type generics class.");
  }
}

class Main {
  public static void main(String[] args) {

    // create an object of GenericsClass
    GenericsClass<String> obj = new GenericsClass<>();
  }
} 
```

在上面的示例中，我们创建了具有**有界类型**的泛型类。 在这里，请注意表达式

```java
<T extends Number> 
```

这意味着`T`只能使用属于`Number`子级的数据类型（`Integer`，`Double`等）。

但是，我们使用`String`创建了泛型类的对象。 这就是为什么当我们运行程序时，会出现以下错误。

```java
GenericsClass<String> obj = new GenericsClass<>();
                                                 ^
    reason: inference variable T has incompatible bounds
      equality constraints: String
      lower bounds: Number
  where T is a type-variable:
    T extends Number declared in class GenericsClass 
```

* * *

## Java 泛型的优点

### 1.代码可重用性

**泛型**允许我们编写适用于不同类型数据的代码。 例如，

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

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

### 2.编译时类型检查

泛型的**类型参数**提供有关泛型代码中使用的数据类型的信息。

因此，可以在编译时识别任何错误，比运行时错误更容易修复。 例如，

```java
// without using Generics
NormalClass list = new NormalClass();

// calls method of NormalClass
list.display("String"); 
```

在上面的代码中，我们有一个普通的类。 我们通过传递字符串数据来调用此类的名为`display()`的方法。

在这里，编译器不知道在参数中传递的值是否正确。 但是，让我们看看如果改用泛型类会发生什么。

```java
// using Generics
GenericsClass<Integer> list = new GenericsClass<>();

// calls method of GenericsClass
list2.display("String");
```

在上面的代码中，我们有一个泛型类。 在这里，类型参数指示该类正在处理`Integer`数据。

因此，当在参数中传递字符串数据时，编译器将生成错误。

### 3.与集合一起使用

集合框架使用 Java 中泛型的概念。 例如，

```java
// creating a string type ArrayList
ArrayList<String> list1 = new ArrayList<>();

// creating a integer type ArrayList
ArrayList<Integer> list2 = new ArrayList<>(); 
```

在上面的示例中，我们使用了相同的`ArrayList`类来处理不同类型的数据。

与`ArrayList`相似，其他集合（`LinkedList`，`Queue`，`Maps`等）在 Java 中也是通用的。