Java 中的菱形运算符

10 Sept 2024 | 4 分钟阅读

Diamond 语法,有时也称为 Diamond Operator,是 Java 7 添加的一项新功能。Diamond Operator 在创建对象时使泛型的使用更加方便。

通过允许隐式重复参数类型规范,它可以防止程序中出现未检查的警告,并减少泛型代码的冗长性。

Java 5 之前的原始类型 (Raw Types)

在 Java 5 之前,集合 API 只支持原始类型。创建集合时,无法指定类型参数。

尽管上述代码可以完美运行,但想象一下您还有以下代码:

现在,列表包含一个不是 String 实例的内容,这会在运行时导致问题。

如果您希望 names 只包含 String,那么您仍然可以使用原始类型,手动检查每个添加项,然后手动将 names 中的每个元素强制转换为 String。

Java 5:泛型

原始类型是指在使用泛型类型时不提供类型参数,这在泛型引入后成为可能。另一方面,List<String> 是参数化类型,而 List 是原始类型。

泛型首次出现在 JDK 1.5 时,为了保持与旧 Java 版本的兼容性,仍然保留了原始类型。

因此,`Object()` 函数现在要求我们指定参数化类型,这可能难以阅读。


编译器会提示一个警告信息,内容为“ArrayList is a raw type. References to ArrayList<E> should be parameterized。”,即使它仍然允许我们在 `Object()` 函数中使用原始类型。

Java 7 的 Diamond Operator

Java 7 中的 Diamond Operator 缩短并简化了这一点。在使用泛型时,它还增强了类型推断,并减少了赋值中的冗长性。

当处理更复杂的数据类型时,例如 Map 对象列表,它会在如下方式中显示出更大的优势:

通过允许编译器推断通用类构造函数的参数类型,Diamond Operator 有助于简化 Java 中围绕泛型的冗长性。这个简单的示例可以在 2009 年 2 月提出的添加到 Java 语言的 Diamond Operator 的初始提案中找到。

请将下面的赋值语句视为一个示例:

由于它更短,因此可以将其替换为以下内容:

上述示例由 Jeremy Manson 的提案(这是对 Project Coin 想法征集的回应的早期提案之一)提供,它很简单但能有效地解释 JDK 7 中 Diamond Operator 的用法。Manson 的提案还提供了关于为什么需要此改进的重要信息。

对类型参数进行不必要的重复的需求,例如

由于类型推断依赖于方法调用,这导致了对静态工厂方法的令人不快的过度依赖。

换句话说,JDK 7 Project Coin 包含的 Diamond Operator 将类型推断扩展到了构造函数,而以前这只对方法可用。当跳过显式参数类别定义时,类型推断会自动与方法一起进行。

另一方面,Diamond Operator 必须显式提供,以“教会”编译器在实例化时进行推断。

Manson 在他的初始提案中指出,由于“为了向后兼容,new Map() 表示一个原始类型,因此不能用于类型推断”,因此缺乏一个特定的 Diamond Operator 阻止了使用语法来隐式推断实例化类型。类型推断和泛型类的实例化是 Java 教程“学习基础 Java 语言”轨迹的“泛型课程”的“类型推断”页面的一个组成部分,该页面已根据 Java SE 7 进行修改。

正如下一节所述,在实例化期间显式指示编译器使用类型推断需要一个特殊的操作符。

请注意,为了从自动类型推断中受益,在实例化泛型类时必须提供 Diamond Operator。下面的示例中,`HashMap()` 函数使用了 `HashMap` 原始类型而不是 `Map<>` 类型,这会导致编译器发出未检查的转换警告。

Josh Bloch 在《Effective Java》第二版的第 24 条“消除未检查警告”中用粗体字强调:“尽可能消除所有未检查的警告。” 当编译使用了声明右侧为原始类型的代码时,就会发生称为未检查转换的警告。Bloch 提供了一个此警告的示例。以下代码列表显示了代码。

下一段代码列表显示了将导致此警告的代码。

结论

简而言之,Diamond Operator 增加了编译器的类型推断功能,并减少了泛型使得赋值的冗长性。