泛型入门
本课属于 Coddy Java 学习旅程的“面向对象编程”单元。第 53 课,共 87 课。
想象一下,你需要创建一个用于存储值并在之后取回它的类。如果没有泛型,你就得为每种类型分别编写一个类:一个用于 Integer,一个用于 String,一个用于 Double,依此类推。这会导致代码重复,也让维护变得麻烦。
泛型通过让你编写适用于任何类型的类和方法,同时保持类型安全,解决了这个问题。你可以定义一个占位符(称为类型参数),使用类时,它会被实际类型替换:
// 不使用泛型:使用 Object,需要类型转换
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0); // 需要手动进行类型转换
// 使用泛型:类型安全,无需类型转换
List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0); // 无需类型转换尖括号 <T> 表示类型参数。当你写 List<String> 时,就是在告诉编译器,这个列表只会包含字符串。如果你尝试添加整数,编译器会立即捕获错误,而不会等到运行时才失败。
泛型有两个主要好处:它们不需要显式类型转换,并且能在编译时而不是运行时发现类型错误。这样可以让你的代码更安全、更简洁。在接下来的课中,我们将探索如何创建自己的泛型类和方法。
挑战
简单我们来构建一个简单的存储系统,通过对比旧方法(使用 Object 并进行类型转换)和新方法(使用泛型并确保类型安全),展示泛型的强大之处。
你将创建 3 个文件,亲自看看它们的区别:
OldBox.java:创建一个类,用泛型出现之前的方式存储值。你的 OldBox 应该有一个类型为Object的私有字段content。添加一个接收 Object 并将其存储起来的构造函数,以及一个返回 Object 的getContent()方法。这种方法可行,但在获取值时需要进行类型转换。GenericBox.java:现在用泛型创建现代且类型安全的版本。你的 GenericBox 声明中应该使用类型参数T。它需要一个类型为T的私有字段content、一个接收T值的构造函数,以及一个返回类型为T的getContent()方法。使用这个类时不需要进行类型转换!Main.java:并排演示这两种方法。你会收到两个输入:一个单词(String)和一个数字(整数)。首先,使用旧方法:创建一个存储该单词的 OldBox,然后将其转换为 String 再取出并打印:
OldBox (with cast): [value]接下来,使用泛型方法:创建一个存储同一单词的
GenericBox<String>,不进行类型转换就取出该单词,并打印:GenericBox (no cast): [value]然后,演示泛型如何处理整数:创建一个存储该数字的
GenericBox<Integer>,取出该数字并打印:GenericBox Integer: [value]最后,打印一个空行,然后打印:
Type safety: Generics catch errors at compile time!
你会依次收到两个输入:一个单词(String)和一个数字(以 String 形式表示的整数,你需要对其进行解析)。
注意关键区别:使用 OldBox 时,你必须进行类型转换 (String) box.getContent(),并希望得到的类型正确。使用 GenericBox 时,编译器知道类型,因此不需要进行类型转换。这就是泛型的核心优势:确保类型安全,同时省去类型转换的麻烦!
亲手试试
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String word = scanner.nextLine();
int number = Integer.parseInt(scanner.nextLine());
// TODO: 创建一个存储 word 的 OldBox
// 将取出的值强制转换为 String,然后打印:"OldBox (with cast): [value]"
// TODO: 创建一个存储 word 的 GenericBox<String>
// 无需强制类型转换,取出值并打印:"GenericBox (no cast): [value]"
// TODO: 创建一个存储 number 的 GenericBox<Integer>
// 取出值并打印:"GenericBox Integer: [value]"
// TODO: 先打印一个空行,然后打印:
// "Type safety: Generics catch errors at compile time!"
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Java 在线编译器