From eb6c5beac5d04cb918940e80507c6956d5885b8b Mon Sep 17 00:00:00 2001 From: Stanley Tam Date: Sat, 17 Aug 2019 00:22:27 +0800 Subject: [PATCH] update --- docs/book/19-Type-Information.md | 178 +++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/docs/book/19-Type-Information.md b/docs/book/19-Type-Information.md index 3cdbfe2..fb83814 100644 --- a/docs/book/19-Type-Information.md +++ b/docs/book/19-Type-Information.md @@ -850,6 +850,184 @@ Manx=7, Rodent=5, Mutt=3, Dog=6, Pet=20, Hamster=1} `instanceof` 有一个严格的限制:只可以将它与命名类型进行比较,而不能与 `Class` 对象作比较。在前面的例子中,你可能会觉得写出一大堆 `instanceof` 表达式很乏味,事实也是如此。但是,也没有办法让 `instanceof` 聪明起来,让它能够自动地创建一个 `Class` 对象的数组,然后将目标与这个数组中的对象逐一进行比较(稍后会看到一种替代方案)。其实这并不是那么大的限制,如果你在程序中写了大量的 `instanceof`,那就说明你的设计可能存在瑕疵。 +## 使用类字面量 + +如果我们使用类字面量重新实现 `PetCreator` 类的话,其结果在很多方面都会更清晰: + + +```java +// typeinfo/pets/LiteralPetCreator.java +// 使用类字面量 +// {java typeinfo.pets.LiteralPetCreator} +package typeinfo.pets; +import java.util.*; + +public class LiteralPetCreator extends PetCreator { + // try 代码块不再需要 + @SuppressWarnings("unchecked") + public static + final List> ALL_TYPES = + Collections.unmodifiableList(Arrays.asList( + Pet.class, Dog.class, Cat.class, Rodent.class, + Mutt.class, Pug.class, EgyptianMau.class, + Manx.class, Cymric.class, Rat.class, + Mouse.class, Hamster.class)); + // 用于随机创建的类型: + private static final + List> TYPES = + ALL_TYPES.subList(ALL_TYPES.indexOf(Mutt.class), + ALL_TYPES.size()); + @Override + public List> types() { + return TYPES; + } + public static void main(String[] args) { + System.out.println(TYPES); + } +} +/* 输出: +[class typeinfo.pets.Mutt, class typeinfo.pets.Pug, +class typeinfo.pets.EgyptianMau, class +typeinfo.pets.Manx, class typeinfo.pets.Cymric, class +typeinfo.pets.Rat, class typeinfo.pets.Mouse, class +typeinfo.pets.Hamster] +*/ +``` + +在即将到来的 `PetCount3.java` 示例中,我们用所有 `Pet` 类型预先加载一个 `Map`(不仅仅是随机生成的),因此 `ALL_TYPES` 类型的列表是必要的。`types` 列表是 `ALL_TYPES` 类型(使用 `List.subList()` 创建)的一部分,它包含精确的宠物类型,因此用于随机生成 `Pet`。 + +这次,`types` 的创建没有被 `try` 块包围,因为它是在编译时计算的,因此不会引发任何异常,不像 `Class.forName()`。 + +我们现在在 `typeinfo.pets` 库中有两个 `PetCreator` 的实现。为了提供第二个作为默认实现,我们可以创建一个使用 `LiteralPetCreator` 的 *外观模式*: + +We now have two implementations of `PetCreator` in the `typeinfo.pets` library. To provide the second one as a default implementation, we can create a *Façade* that utilizes `LiteralPetCreator`: + +```java +// typeinfo/pets/Pets.java +// Facade to produce a default PetCreator +package typeinfo.pets; +import java.util.*; +import java.util.stream.*; + +public class Pets { + public static final PetCreator CREATOR = + new LiteralPetCreator(); + + public static Pet get() { + return CREATOR.get(); + } + + public static Pet[] array(int size) { + Pet[] result = new Pet[size]; + for(int i = 0; i < size; i++) + result[i] = CREATOR.get(); + return result; + } + + public static List list(int size) { + List result = new ArrayList<>(); + Collections.addAll(result, array(size)); + return result; + } + + public static Stream stream() { + return Stream.generate(CREATOR); + } +} +``` + +这还提供了对 `get()`、`array()` 和 `list()` 的间接调用,以及生成 `Stream` 的新方法。 + +因为 `PetCount.countPets()` 采用了 `PetCreator` 参数,所以我们可以很容易地测试 `LiteralPetCreator`(通过上面的外观模式): + +```java +// typeinfo/PetCount2.java +import typeinfo.pets.*; + +public class PetCount2 { + public static void main(String[] args) { + PetCount.countPets(Pets.CREATOR); + } +} +/* 输出: +Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat +EgyptianMau Hamster EgyptianMau Mutt Mutt Cymric Mouse +Pug Mouse Cymric +{EgyptianMau=2, Pug=3, Rat=2, Cymric=5, Mouse=2, Cat=9, +Manx=7, Rodent=5, Mutt=3, Dog=6, Pet=20, Hamster=1} +*/ +``` + +输出与 `PetCount.java` 的输出相同。 + +## 一个动态 `instanceof` 函数 + +`Class.isInstance()` 方法提供了一种动态测试对象类型的方法。因此,所有这些繁琐的 `instanceof` 语句都可以从 `PetCount.java` 中删除: + +```java +// typeinfo/PetCount3.java +// 使用 isInstance() 方法 +import java.util.*; +import java.util.stream.*; +import onjava.*; +import typeinfo.pets.*; + +public class PetCount3 { + static class Counter extends + LinkedHashMap, Integer> { + + Counter() { + super(LiteralPetCreator.ALL_TYPES.stream() + .map(lpc -> Pair.make(lpc, 0)) + .collect( + Collectors.toMap(Pair::key, Pair::value))); + } + + public void count(Pet pet) { + // Class.isInstance() 替换 instanceof: + entrySet().stream() + .filter(pair -> pair.getKey().isInstance(pet)) + .forEach(pair -> + put(pair.getKey(), pair.getValue() + 1)); + } + + @Override + public String toString() { + String result = entrySet().stream() + .map(pair -> String.format("%s=%s", + pair.getKey().getSimpleName(), + pair.getValue())) + .collect(Collectors.joining(", ")); + return "{" + result + "}"; + } + } + + public static void main(String[] args) { + Counter petCount = new Counter(); + Pets.stream() + .limit(20) + .peek(petCount::count) + .forEach(p -> System.out.print( + p.getClass().getSimpleName() + " ")); + System.out.println("\n" + petCount); + } +} +/* 输出: +Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat +EgyptianMau Hamster EgyptianMau Mutt Mutt Cymric Mouse +Pug Mouse Cymric +{Rat=2, Pug=3, Mutt=3, Mouse=2, Cat=9, Dog=6, Cymric=5, +EgyptianMau=2, Rodent=5, Hamster=1, Manx=7, Pet=20} +*/ +``` + +为了计算所有不同类型的 `Pet`,`Counter Map` 预先加载了来自 `LiteralPetCreator.ALL_TYPES` 的类型。如果不预先加载 `Map`,将只计数随机生成的类型,而不是像 `Pet` 和 `Cat` 这样的基本类型。 + +`isInstance()` 方法消除了对 `instanceof` 表达式的需要。此外,这意味着你可以通过更改 `LiteralPetCreator.types` 数组来添加新类型的 `Pet`;程序的其余部分不需要修改(就像使用 `instanceof` 表达式时那样)。 + +`toString()` 方法被重载,以便更容易读取输出,该输出仍与打印 `Map` 时看到的典型输出匹配。 + +### Counting Recursively ## 类型转换检测