From ddbb2ada308764b1ca5b7d3a63a274f57ae6a6b0 Mon Sep 17 00:00:00 2001 From: 1326670425 <1326670425@qq.com> Date: Wed, 13 Nov 2019 12:57:22 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AC=AC=E4=BA=8C=E5=8D=81=E7=AB=A0=20?= =?UTF-8?q?=E6=B3=9B=E5=9E=8B=20=E6=A0=A1=E8=AE=A2+=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=20=E8=BE=B9=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/book/20-Generics.md | 315 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 309 insertions(+), 6 deletions(-) diff --git a/docs/book/20-Generics.md b/docs/book/20-Generics.md index 938b5f9f..daf2969d 100644 --- a/docs/book/20-Generics.md +++ b/docs/book/20-Generics.md @@ -17,7 +17,7 @@ 然而,如果你了解其他语言(例如 C++ )的参数化机制,你会发现,Java 泛型并不能满足所有的预期。使用别人创建好的泛型相对容易,但是创建自己的泛型时,就会遇到很多意料之外的麻烦。 -这并不是说 Java 泛型毫无用处。在很多情况下,它可以使代码更直接更优雅。不过,如果你见识过那种实现了更纯粹的泛型的编程语言,那么,Java 可能会令你失望。本章会介绍 Java 泛型的优点与局限。我会解释 Java 的泛型是如何发展成现在这样的,希望能够帮助你更有效地使用这个特性。 +这并不是说 Java 泛型毫无用处。在很多情况下,它可以使代码更直接更优雅。不过,如果你见识过那种实现了更纯粹的泛型的编程语言,那么,Java 可能会令你失望。本章会介绍 Java 泛型的优点与局限。我会解释 Java 的泛型是如何发展成现在这样的,希望能够帮助你更有效地使用这个特性。[^1] ### 与 C++ 的比较 @@ -1756,13 +1756,317 @@ Note: Recompile with -Xlint:unchecked for details. 果然,标准库会产生很多警告。如果你使用过 C 语言,尤其是使用 ANSI C 之前的语言,你会记住警告的特殊效果:发现警告后,可以忽略它们。因此,除非程序员必须对其进行处理,否则最好不要从编译器发出任何类型的消息。 -Neal Gafter(Java 5的主要开发人员之一)在他的博客中[^2]指出,他在重写 Java 库时很懒惰,我们不应该做他所做的事情。Neal 还指出,他在不破坏现有接口的情况下无法修复某些 Java 库代码。因此,即使某些习惯用法出现在 Java 库源代码中,也不一定是正确的做法。当查看库代码时,我们不能假设这是您在自己的代码中必须要遵循的示例。 +Neal Gafter(Java 5的主要开发人员之一)在他的博客中[^2]指出,他在重写 Java 库时是很随意、马虎的,我们不应该像他那样做。Neal 还指出,他在不破坏现有接口的情况下无法修复某些 Java 库代码。因此,即使在 Java 库源代码中出现了一些习惯用法,它们也不一定是正确的做法。当查看库代码时,我们不能认为这就是要在自己代码中必须遵循的示例。 请注意,在 Java 文献中推荐使用类型标记技术,例如 Gilad Bracha 的论文《Generics in the Java Programming Language》[^3],他指出:“例如,这种用法已广泛用于新的 API 中以处理注解。” 我发现此技术在人们对于舒适度的看法方面存在一些不一致之处;有些人强烈喜欢本章前面介绍的工厂方法。 ## 边界 +*边界*(bounds)在本章的前面进行了简要介绍。边界允许我们对泛型使用的参数类型施加约束。尽管这可以强制执行有关应用了泛型类型的规则,但潜在的更重要的效果是我们可以在绑定的类型中调用方法。 + +由于擦除会删除类型信息,因此唯一可用于无限制泛型参数的方法是那些 **Object** 可用的方法。但是,如果将该参数限制为某类型的子集,则可以调用该子集中的方法。为了应用约束,Java 泛型使用了 `extends` 关键字。 + +重要的是要理解,当用于限定泛型类型时,`extends` 的含义与通常的意义截然不同。此示例展示边界的基础应用: + +```java +// generics/BasicBounds.java + +interface HasColor { + java.awt.Color getColor(); +} + +class WithColor { + T item; + + WithColor(T item) { + this.item = item; + } + + T getItem() { + return item; + } + + // The bound allows you to call a method: + java.awt.Color color() { + return item.getColor(); + } +} + +class Coord { + public int x, y, z; +} + +// This fails. Class must be first, then interfaces: +// class WithColorCoord { + +// Multiple bounds: +class WithColorCoord { + T item; + + WithColorCoord(T item) { + this.item = item; + } + + T getItem() { + return item; + } + + java.awt.Color color() { + return item.getColor(); + } + + int getX() { + return item.x; + } + + int getY() { + return item.y; + } + + int getZ() { + return item.z; + } +} + +interface Weight { + int weight(); +} + +// As with inheritance, you can have only one +// concrete class but multiple interfaces: +class Solid { + T item; + + Solid(T item) { + this.item = item; + } + + T getItem() { + return item; + } + + java.awt.Color color() { + return item.getColor(); + } + + int getX() { + return item.x; + } + + int getY() { + return item.y; + } + + int getZ() { + return item.z; + } + + int weight() { + return item.weight(); + } +} + +class Bounded + extends Coord implements HasColor, Weight { + @Override + public java.awt.Color getColor() { + return null; + } + + @Override + public int weight() { + return 0; + } +} + +public class BasicBounds { + public static void main(String[] args) { + Solid solid = + new Solid<>(new Bounded()); + solid.color(); + solid.getY(); + solid.weight(); + } +} +``` + +你可能会观察到 **BasicBounds.java** 中似乎包含一些冗余,它们可以通过继承来消除。在这里,每个继承级别还添加了边界约束: + +```java +// generics/InheritBounds.java + +class HoldItem { + T item; + + HoldItem(T item) { + this.item = item; + } + + T getItem() { + return item; + } +} + +class WithColor2 + extends HoldItem { + WithColor2(T item) { + super(item); + } + + java.awt.Color color() { + return item.getColor(); + } +} + +class WithColorCoord2 + extends WithColor2 { + WithColorCoord2(T item) { + super(item); + } + + int getX() { + return item.x; + } + + int getY() { + return item.y; + } + + int getZ() { + return item.z; + } +} + +class Solid2 + extends WithColorCoord2 { + Solid2(T item) { + super(item); + } + + int weight() { + return item.weight(); + } +} + +public class InheritBounds { + public static void main(String[] args) { + Solid2 solid2 = + new Solid2<>(new Bounded()); + solid2.color(); + solid2.getY(); + solid2.weight(); + } +} +``` + +**HoldItem** 拥有一个对象,因此此行为将继承到 **WithColor2** 中,这也需要其参数符合 **HasColor**。 **WithColorCoord2** 和 **Solid2** 进一步扩展了层次结构,并在每个级别添加了边界。现在,这些方法已被继承,并且在每个类中不再重复。 + +这是一个具有更多层次的示例: + +```java +// generics/EpicBattle.java +// Bounds in Java generics + +import java.util.List; + +interface SuperPower { +} + +interface XRayVision extends SuperPower { + void seeThroughWalls(); +} + +interface SuperHearing extends SuperPower { + void hearSubtleNoises(); +} + +interface SuperSmell extends SuperPower { + void trackBySmell(); +} + +class SuperHero { + POWER power; + + SuperHero(POWER power) { + this.power = power; + } + + POWER getPower() { + return power; + } +} + +class SuperSleuth + extends SuperHero { + SuperSleuth(POWER power) { + super(power); + } + + void see() { + power.seeThroughWalls(); + } +} + +class +CanineHero + extends SuperHero { + CanineHero(POWER power) { + super(power); + } + + void hear() { + power.hearSubtleNoises(); + } + + void smell() { + power.trackBySmell(); + } +} + +class SuperHearSmell + implements SuperHearing, SuperSmell { + @Override + public void hearSubtleNoises() { + } + + @Override + public void trackBySmell() { + } +} + +class DogPerson extends CanineHero { + DogPerson() { + super(new SuperHearSmell()); + } +} + +public class EpicBattle { + // Bounds in generic methods: + static + void useSuperHearing(SuperHero hero) { + hero.getPower().hearSubtleNoises(); + } + + static + void superFind(SuperHero hero) { + hero.getPower().hearSubtleNoises(); + hero.getPower().trackBySmell(); + } + + public static void main(String[] args) { + DogPerson dogPerson = new DogPerson(); + useSuperHearing(dogPerson); + superFind(dogPerson); + // You can do this: + List audioPeople; + // But you can't do this: + // List dogPs; + } +} +``` + +接下来将要研究的通配符将会把范围限制在单个类型。 ## 通配符 @@ -1805,10 +2109,9 @@ Neal Gafter(Java 5的主要开发人员之一)在他的博客中[^2]指出 - - - - +[^1]: 在编写本章期间,Angelika Langer的 Java 泛型常见问题解答以及她的其他著作(与Klaus Kreft一起)是非常宝贵的。 +[^2]: [http://gafter.blogspot.com/2004/09/puzzling-through-erasureanswer.html](http://gafter.blogspot.com/2004/09/puzzling-through-erasureanswer.html) +[^3]: 参见本章章末引文。