原文: https://www.programiz.com/java-programming/generics

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

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

注意泛型不适用于基本类型(intfloatchar等)。


泛型的工作原理

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

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

  1. import java.util.ArrayList;
  2. class Main {
  3. public static void main(String[] args) {
  4. // create an array list to store Integer data
  5. ArrayList<Integer> list1 = new ArrayList<>();
  6. list1.add(4);
  7. list1.add(5);
  8. System.out.println("ArrayList of Integer: " + list1);
  9. // creates an array list to store String data
  10. ArrayList<String> list2 = new ArrayList<>();
  11. list2.add("Four");
  12. list2.add("Five");
  13. System.out.println("ArrayList of String: " + list2);
  14. // creates an array list to store Double data
  15. ArrayList<Double> list3 = new ArrayList<>();
  16. list3.add(4.5);
  17. list3.add(6.5);
  18. System.out.println("ArrayList of Double: " + list3);
  19. }
  20. }

输出量

  1. ArrayList of Integer: [4, 5]
  2. ArrayList of String: [Four, Five]
  3. ArrayList of Double: [4.5, 6.5]

在上面的示例中,我们使用了相同的ArrayList类来存储IntegerStringDouble类型的元素。 由于 Java 泛型的缘故,这是可能的。

在这里,请注意这行,

  1. ArrayList<Integer> list1 = new ArrayList<>();

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

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


创建泛型类

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

示例:创建泛型类

  1. class Main {
  2. public static void main(String[] args) {
  3. // initialize generic class with Integer data
  4. GenericsClass<Integer> intObj = new GenericsClass<>(5);
  5. System.out.println("Generic Class returns: " + intObj.getData());
  6. // initialize generic class with String data
  7. GenericsClass<String> stringObj = new GenericsClass<>("Java Programming");
  8. System.out.println("Generic Class returns: " + stringObj.getData());
  9. }
  10. }
  11. class GenericsClass<T> {
  12. // variable of T type
  13. private T data;
  14. public GenericsClass(T data) {
  15. this.data = data;
  16. }
  17. // method that return T type variable
  18. public T getData() {
  19. return this.data;
  20. }
  21. }

Output

  1. Generic Class returns: 5
  2. Generic Class returns: Java Programing

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

  1. class GenericsClass<T> {...}

在此,T表示类型参数。 在Main类内部,我们创建了名为intObjstringObjGenericsClass对象。

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

创建泛型方法

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

示例:创建泛型方法

  1. class Main {
  2. public static void main(String[] args) {
  3. // initialize the class with Integer data
  4. DemoClass demo = new DemoClass();
  5. demo.<String>genericsMethod("Java Programming");
  6. }
  7. }
  8. class DemoClass {
  9. // generics method
  10. public <T> void genericsMethod(T data) {
  11. System.out.println("This is a generics method.");
  12. System.out.println("The data passed to method is " + data);
  13. }
  14. }

输出

  1. This is a generics method.
  2. The data passed to the method: Java Programming

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

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

在此,将类型参数<T>插入到修饰符(public)之后和返回类型(void)之前。

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

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

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

  1. demo.genericsMethod("Java Programming");

有界类型

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

我们使用extends关键字。 例如,

  1. <T extends A>

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

示例:有界类型

  1. class GenericsClass <T extends Number> {
  2. public void display() {
  3. System.out.println("This is a bounded type generics class.");
  4. }
  5. }
  6. class Main {
  7. public static void main(String[] args) {
  8. // create an object of GenericsClass
  9. GenericsClass<String> obj = new GenericsClass<>();
  10. }
  11. }

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

  1. <T extends Number>

这意味着T只能使用属于Number子级的数据类型(IntegerDouble等)。

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

  1. GenericsClass<String> obj = new GenericsClass<>();
  2. ^
  3. reason: inference variable T has incompatible bounds
  4. equality constraints: String
  5. lower bounds: Number
  6. where T is a type-variable:
  7. T extends Number declared in class GenericsClass

Java 泛型的优点

1.代码可重用性

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

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

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

2.编译时类型检查

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

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

  1. // without using Generics
  2. NormalClass list = new NormalClass();
  3. // calls method of NormalClass
  4. list.display("String");

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

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

  1. // using Generics
  2. GenericsClass<Integer> list = new GenericsClass<>();
  3. // calls method of GenericsClass
  4. list2.display("String");

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

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

3.与集合一起使用

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

  1. // creating a string type ArrayList
  2. ArrayList<String> list1 = new ArrayList<>();
  3. // creating a integer type ArrayList
  4. ArrayList<Integer> list2 = new ArrayList<>();

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

ArrayList相似,其他集合(LinkedListQueueMaps等)在 Java 中也是通用的。