介绍
Java 中的泛型(Generics)通过提供额外的抽象层,使得编写灵活且可重用的代码成为可能。它们允许为多种类型的对象创建单一算法。泛型提供了类型安全性,并避免了运行时错误。在本实验中,我们将了解 Java 中泛型的各个方面。
Java 中的泛型(Generics)通过提供额外的抽象层,使得编写灵活且可重用的代码成为可能。它们允许为多种类型的对象创建单一算法。泛型提供了类型安全性,并避免了运行时错误。在本实验中,我们将了解 Java 中泛型的各个方面。
我们可以借助菱形操作符(<>)在 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 中的泛型。