Java 泛型概念

JavaJavaBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

介绍

Java 中的泛型(Generics)通过提供额外的抽象层,使得编写灵活且可重用的代码成为可能。它们允许为多种类型的对象创建单一算法。泛型提供了类型安全性,并避免了运行时错误。在本实验中,我们将了解 Java 中泛型的各个方面。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL java(("`Java`")) -.-> java/BasicSyntaxGroup(["`Basic Syntax`"]) java(("`Java`")) -.-> java/DataStructuresGroup(["`Data Structures`"]) java(("`Java`")) -.-> java/ProgrammingTechniquesGroup(["`Programming Techniques`"]) java(("`Java`")) -.-> java/ObjectOrientedandAdvancedConceptsGroup(["`Object-Oriented and Advanced Concepts`"]) java/BasicSyntaxGroup -.-> java/output("`Output`") java/BasicSyntaxGroup -.-> java/type_casting("`Type Casting`") java/DataStructuresGroup -.-> java/arrays("`Arrays`") java/ProgrammingTechniquesGroup -.-> java/method_overloading("`Method Overloading`") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/classes_objects("`Classes/Objects`") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/arraylist("`ArrayList`") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/generics("`Generics`") subgraph Lab Skills java/output -.-> lab-117688{{"`Java 泛型概念`"}} java/type_casting -.-> lab-117688{{"`Java 泛型概念`"}} java/arrays -.-> lab-117688{{"`Java 泛型概念`"}} java/method_overloading -.-> lab-117688{{"`Java 泛型概念`"}} java/classes_objects -.-> lab-117688{{"`Java 泛型概念`"}} java/arraylist -.-> lab-117688{{"`Java 泛型概念`"}} java/generics -.-> lab-117688{{"`Java 泛型概念`"}} end

创建泛型类

我们可以借助菱形操作符(<>)在 Java 中创建一个泛型类。在以下代码块中,我们将创建一个名为 MyGenericClass 的泛型类,它可以接受任何类型的对象。我们还将创建一个 MyGenericClass 类型的对象,并使用整数和字符串类型作为参数。

// ~/project/MyGenericClass.java

class MyGenericClass<T> {
    T field1;

    public MyGenericClass(T field1) {
        this.field1 = field1;
    }

    public T getField1() {
        return field1;
    }
}

public class Main {
    public static void main(String[] args) {
        MyGenericClass<Integer> myIntObj = new MyGenericClass<Integer>(100);
        MyGenericClass<String> myStringObj = new MyGenericClass<String>("Hello World");

        System.out.println(myIntObj.getField1());
        System.out.println(myStringObj.getField1());
    }
}

要运行代码,请打开终端并导航到 ~/project 目录。使用以下命令编译代码:

javac MyGenericClass.java

成功编译后,使用以下命令运行代码:

java Main

代码的输出将是:

100
Hello World

创建泛型方法

我们也可以在 Java 中创建泛型方法。泛型方法可以处理不同类型的对象。在以下代码块中,我们将创建一个名为 printArray 的泛型方法,它可以打印任何类型的数组。

// ~/project/Main.java

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

    public static void main(String[] args) {
        Integer[] intArray = {1, 2, 3, 4, 5};
        Double[] doubleArray = {1.1, 2.2, 3.3, 4.4};
        String[] stringArray = {"Hello", "World"};

        printArray(intArray);
        printArray(doubleArray);
        printArray(stringArray);
    }
}

要运行代码,请打开终端并导航到 ~/project 目录。使用以下命令编译代码:

javac Main.java

成功编译后,使用以下命令运行代码:

java Main

代码的输出将是:

1
2
3
4
5
1.1
2.2
3.3
4.4
Hello
World

创建有界泛型方法

我们还可以创建有界泛型方法。有界泛型限制了方法接受的类型范围。我们使用 extends 关键字来施加这种限制。在以下代码块中,我们将创建一个名为 printNumbers 的有界泛型方法,它可以打印 Number 类型及其子类对象的数字。

// ~/project/Main.java

public class Main {
    public static <T extends Number> void printNumbers(T[] numbers) {
        for (T number : numbers)
            System.out.println(number);
    }

    public static void main(String[] args) {
        Integer[] intNumbers = {1, 2, 3, 4, 5};
        Double[] doubleNumbers = {1.1, 2.2, 3.3, 4.4};

        printNumbers(intNumbers);
        printNumbers(doubleNumbers);
    }
}

要运行代码,请打开终端并导航到 ~/project 目录。使用以下命令编译代码:

javac Main.java

成功编译后,使用以下命令运行代码:

java Main

代码的输出将是:

1
2
3
4
5
1.1
2.2
3.3
4.4

理解泛型中的类型安全性

泛型提供了类型安全性,确保我们一次只能定义一种类型的对象来操作。在以下代码块中,我们将创建一个未定义类型参数的整数 ArrayList。不使用类型参数来使用泛型是一种不良实践。代码可以编译,但在尝试访问其他类型的元素时会返回运行时错误。

// ~/project/Main.java

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList integerList = new ArrayList();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
        integerList.add("Hello World");

        for (Object element : integerList) {
            System.out.println((Integer)element);
        }
    }
}

要运行代码,请打开终端并导航到 ~/project 目录。使用以下命令编译代码:

javac Main.java

成功编译后,使用以下命令运行代码:

java Main

代码的输出将是:

1
2
3
Exception in thread "main" java.lang.ClassCastException: java.base/java.lang.String cannot be cast to java.base/java.lang.Integer
    at Main.main(Main.java:13)

我们得到一个运行时错误,因为 integerList 包含了 Integer 类型和 String 类型的元素。这是因为我们在创建 integerList 时没有定义类型参数。

理解泛型中的类型擦除

Java 在泛型中使用类型擦除(Type Erasure)来确保在运行时不需要额外的开销。在类型擦除过程中,编译器会将类型参数替换为 Object 类或父类(在有界泛型的情况下)。在以下代码块中,我们将看到泛型在编译时和运行时如何处理原始类型。

// ~/project/Main.java

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<Integer> integerList = new ArrayList<Integer>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);

        int sum = 0;
        for (int i = 0; i < integerList.size(); i++) {
            sum += integerList.get(i);
        }
        System.out.println(sum);
    }
}

要运行代码,请打开终端并导航到 ~/project 目录。使用以下命令编译代码:

javac Main.java

成功编译后,使用以下命令运行代码:

java Main

代码的输出将是:

6

在上述代码块中,我们尝试对 integerList 中的元素进行求和。我们知道,像 int 这样的原始类型不能直接与泛型一起使用。由于类型擦除,类型参数 Integer 在编译时被替换为 Object 类。然而,自动装箱(autoboxing)和拆箱(unboxing)允许我们在编译时执行整数的加法操作。在运行时,代码像普通代码一样执行,没有任何额外的开销。

总结

在本实验中,我们学习了 Java 泛型。我们涵盖了与泛型类、泛型方法、有界泛型方法、泛型中的类型安全性以及泛型中的类型擦除相关的基本概念。我们还看到了如何使用泛型编写灵活且可重用的代码。希望本实验能帮助你理解 Java 中的泛型。

您可能感兴趣的其他 Java 教程