@@ -551,7 +551,305 @@ public class ClassCasts {
551551
552552Java类库中另一个没有任何用处的特性就是 ` Class.asSubclass() ` ,该方法允许你将一个 ` Class ` 对象转型为更加具体的类型。
553553
554- <!-- Checking Before a Cast -->
554+ ## 类型转换前先做检查
555+
556+ 直到现在,我们已知的RTTI类型包括:
557+
558+ 1 . 传统的类型转换,如 “` (Shape) ` ”,由RTTI确保转换的正确性,如果执行了一个错误的类型转换,就会抛出一个 ` ClassCastException ` 异常。
559+
560+ 2 . 代表对象类型的 ` Class ` 对象. 通过查询 ` Class ` 对象可以获取运行时所需的信息.
561+
562+ 在C++中,经典的类型转换 “`(Shape)`” 并不使用 RTTI. 它只是简单地告诉编译器将这个对象作为新的类型对待. 而 Java 会进行类型检查,这种类型转换一般被称作“类型安全的向下转型”。之所以称作“向下转型”,是因为传统上类继承图是这么画的。将 `Circle` 转换为 `Shape` 是一次向上转型, 将 `Shape` 转换为 `Circle` 是一次向下转型。但是, 因为我们知道 `Circle` 肯定是一个 `Shape`,所以编译器允许我们自由地做向上转型的赋值操作,且不需要任何显示的转型操作。当你给编译器一个 `Shape` 的时候,编译器并不知道它到底是什么类型的 `Shape`——它可能是 `Shape`,也可能是 `Shape` 的子类型,例如 `Circle`、`Square`、`Triangle` 或某种其他的类型。在编译期,编译器只能知道它是 `Shape`。因此,你需要使用显式的类型转换,以告知编译器你想转换的特定类型,否则编译器就不允许你执行向下转型赋值。 (编译器将会检查向下转型是否合理,因此它不允许向下转型到实际上不是待转型类型的子类的类型上)。
563+
564+ RTTI 在 Java 中还有第三种形式,那就是关键字 ` instanceof ` 。它返回一个布尔值,告诉我们对象是不是某个特定类型的实例,可以用提问的方式使用它,就像这个样子:
565+
566+ ``` java
567+ if (x instanceof Dog )
568+ ((Dog )x). bark();
569+ ```
570+
571+ 在将 ` x ` 转型为 ` Dog ` 之前,` if ` 语句会先检查 ` x ` 是否是 ` Dog ` 类型的对象。进行向下转型前,如果没有其他信息可以告诉你这个对象是什么类型,那么使用 ` instanceof ` 是非常重要的,否则会得到一个 ` ClassCastException ` 异常。
572+
573+ 一般,可能想要查找某种类型(比如要找三角形,并填充为紫色),这时可以轻松地使用 ` instanceof ` 来计数所有对象。举个例子,假如你有一个类的继承体系,描述了 ` Pet ` (以及它们的主人,在后面一个例子中会用到这个特性)。在这个继承体系中的每个 ` Individual ` 都有一个 ` id ` 和一个可选的名字。尽管下面的类都继承自 ` Individual ` ,但是 ` Individual ` 类复杂性较高,因此其代码将放在[ 附录:容器] ( ./Appendix-Collection-Topics ) 中进行解释说明。正如你所看到的,此处并不需要去了解 ` Individual ` 的代码——你只需了解你可以创建其具名或不具名的对象,并且每个 ` Individual ` 都有一个 ` id() ` 方法,如果你没有为 ` Individual ` 提供名字,` toString() ` 方法只产生类型名。
574+
575+ 下面是继承自 ` Individual ` 的类的继承体系:
576+
577+ ``` java
578+ // typeinfo/pets/Person.java
579+ package typeinfo.pets ;
580+
581+ public class Person extends Individual {
582+ public Person (String name ) { super (name); }
583+ }
584+ ```
585+
586+ ``` java
587+ // typeinfo/pets/Pet.java
588+ package typeinfo.pets ;
589+
590+ public class Pet extends Individual {
591+ public Pet (String name ) { super (name); }
592+ public Pet () { super (); }
593+ }
594+ ```
595+
596+ ``` java
597+ // typeinfo/pets/Dog.java
598+ package typeinfo.pets ;
599+
600+ public class Dog extends Pet {
601+ public Dog (String name ) { super (name); }
602+ public Dog () { super (); }
603+ }
604+ ```
605+
606+ ``` java
607+ // typeinfo/pets/Mutt.java
608+ package typeinfo.pets ;
609+
610+ public class Mutt extends Dog {
611+ public Mutt (String name ) { super (name); }
612+ public Mutt () { super (); }
613+ }
614+ ```
615+
616+
617+ ``` java
618+ // typeinfo/pets/Pug.java
619+ package typeinfo.pets ;
620+
621+ public class Pug extends Dog {
622+ public Pug (String name ) { super (name); }
623+ public Pug () { super (); }
624+ }
625+ ```
626+
627+ ``` java
628+ // typeinfo/pets/Cat.java
629+ package typeinfo.pets ;
630+
631+ public class Cat extends Pet {
632+ public Cat (String name ) { super (name); }
633+ public Cat () { super (); }
634+ }
635+ ```
636+
637+ ``` java
638+ // typeinfo/pets/EgyptianMau.java
639+ package typeinfo.pets ;
640+
641+ public class EgyptianMau extends Cat {
642+ public EgyptianMau (String name ) { super (name); }
643+ public EgyptianMau () { super (); }
644+ }
645+ ```
646+
647+ ``` java
648+ // typeinfo/pets/Manx.java
649+ package typeinfo.pets ;
650+
651+ public class Manx extends Cat {
652+ public Manx (String name ) { super (name); }
653+ public Manx () { super (); }
654+ }
655+ ```
656+
657+ ``` java
658+ // typeinfo/pets/Cymric.java
659+ package typeinfo.pets ;
660+
661+ public class Cymric extends Manx {
662+ public Cymric (String name ) { super (name); }
663+ public Cymric () { super (); }
664+ }
665+ ```
666+
667+ ``` java
668+ // typeinfo/pets/Rodent.java
669+ package typeinfo.pets ;
670+
671+ public class Rodent extends Pet {
672+ public Rodent (String name ) { super (name); }
673+ public Rodent () { super (); }
674+ }
675+ ```
676+
677+ ``` java
678+ // typeinfo/pets/Rat.java
679+ package typeinfo.pets ;
680+
681+ public class Rat extends Rodent {
682+ public Rat (String name ) { super (name); }
683+ public Rat () { super (); }
684+ }
685+ ```
686+
687+ ``` java
688+ // typeinfo/pets/Mouse.java
689+ package typeinfo.pets ;
690+
691+ public class Mouse extends Rodent {
692+ public Mouse (String name ) { super (name); }
693+ public Mouse () { super (); }
694+ }
695+ ```
696+
697+ ``` java
698+ // typeinfo/pets/Hamster.java
699+ package typeinfo.pets ;
700+
701+ public class Hamster extends Rodent {
702+ public Hamster (String name ) { super (name); }
703+ public Hamster () { super (); }
704+ }
705+ ```
706+
707+ 我们必须显式地为每一个子类编写无参构造器。因为我们有一个带一个参数的构造器,所以编译器不会自动地为我们加上无参构造器。
708+
709+ 接下来,我们需要一个类,它可以随机地创建不同类型的宠物,同时,它还可以创建宠物数组和持有宠物的 ` List ` 。为了这个类更加普遍适用,我们将其定义为抽象类:
710+
711+ ``` java
712+ // typeinfo/pets/PetCreator.java
713+ // Creates random sequences of Pets
714+ package typeinfo.pets ;
715+ import java.util.* ;
716+ import java.util.function.* ;
717+
718+ public abstract
719+ class PetCreator implements Supplier<Pet > {
720+ private Random rand = new Random (47 );
721+ // The List of the different types of Pet to create:
722+ public abstract List<Class<? extends Pet > > types ();
723+ public Pet get () { // Create one random Pet
724+ int n = rand. nextInt(types(). size());
725+ try {
726+ return types(). get(n). newInstance();
727+ } catch (InstantiationException |
728+ IllegalAccessException e) {
729+ throw new RuntimeException (e);
730+ }
731+ }
732+ }
733+ ```
734+
735+ 抽象的 ` types() ` 方法需要子类来实现,以此来获取 ` Class ` 对象构成的 ` List ` (这是模板方法设计模式的一种变体)。注意,其中类的类型被定义为“任何从 ` Pet ` 导出的类型”,因此 ` newInstance() ` 不需要转型就可以产生 ` Pet ` 。` get() ` 随机的选取出一个 ` Class ` 对象,然后可以通过 ` Class.newInstance() ` 来生成该类的新实例。
736+
737+ 在调用 ` newInstance() ` 时,可能会出现两种异常。在紧跟 ` try ` 语句块后面的 ` catch ` 子句中可以看到对它们的处理。异常的名字再次成为了一种对错误类型相对比较有用的解释(` IllegalAccessException ` 违反了 Java 安全机制,在本例中,表示默认构造器为 ` private ` 的情况)。
738+
739+ 当你导出 ` PetCreator ` 的子类时,你需要为 ` get() ` 方法提供 ` Pet ` 类型的 ` List ` 。` types() ` 方法会简单地返回一个静态 ` List ` 的引用。下面是使用 ` forName() ` 的一个具体实现:
740+
741+ ``` java
742+ // typeinfo/pets/ForNameCreator.java
743+ package typeinfo.pets ;
744+ import java.util.* ;
745+
746+ public class ForNameCreator extends PetCreator {
747+ private static List<Class<? extends Pet > > types =
748+ new ArrayList<> ();
749+ // Types you want randomly created:
750+ private static String [] typeNames = {
751+ " typeinfo.pets.Mutt" ,
752+ " typeinfo.pets.Pug" ,
753+ " typeinfo.pets.EgyptianMau" ,
754+ " typeinfo.pets.Manx" ,
755+ " typeinfo.pets.Cymric" ,
756+ " typeinfo.pets.Rat" ,
757+ " typeinfo.pets.Mouse" ,
758+ " typeinfo.pets.Hamster"
759+ };
760+ @SuppressWarnings (" unchecked" )
761+ private static void loader () {
762+ try {
763+ for (String name : typeNames)
764+ types. add(
765+ (Class<? extends Pet > )Class . forName(name));
766+ } catch (ClassNotFoundException e) {
767+ throw new RuntimeException (e);
768+ }
769+ }
770+ static { loader(); }
771+ @Override
772+ public List<Class<? extends Pet > > types () {
773+ return types;
774+ }
775+ }
776+ ```
777+
778+ ` loader() ` 方法使用 ` Class.forName() ` 创建了 ` Class ` 对象的 ` List ` 。这可能会导致 ` ClassNotFoundException ` ,因为你传入的是一个 ` String ` ,它不能再编译期间被确认是否合理。由于 ` Pet ` 相关的文件在 ` typeinfo ` 包里面,所以使用它们的时候需要填写完整的包名。
779+
780+ 为了使得 ` List ` 装入的是具体的 ` Class ` 对象,转型是必须的,它会产生一个编译时警告。` loader() ` 方法是分开编写的,然后它被放入到一个静态代码块里,因为 ` @SuppressWarning ` 注解不能够直接放置在静态代码块之上。
781+
782+ 为了对 ` Pet ` 进行计数,我们需要一个能跟踪不同类型的 ` Pet ` 的工具。` Map ` 的是这个需求的首选,我们将 ` Pet ` 类型名作为键,将保存 ` Pet ` 数量的 ` Integer ` 作为值。通过这种方式,你就看可以询问:“有多少个 ` Hamster ` 对象?”我们可以使用 ` instanceof ` 来对 ` Pet ` 进行计数:
783+
784+ ``` java
785+ // typeinfo/PetCount.java
786+ // Using instanceof
787+ import typeinfo.pets.* ;
788+ import java.util.* ;
789+
790+ public class PetCount {
791+ static class Counter extends HashMap<String ,Integer > {
792+ public void count (String type ) {
793+ Integer quantity = get(type);
794+ if (quantity == null )
795+ put(type, 1 );
796+ else
797+ put(type, quantity + 1 );
798+ }
799+ }
800+ public static void
801+ countPets (PetCreator creator ) {
802+ Counter counter = new Counter ();
803+ for (Pet pet : Pets . array(20 )) {
804+ // List each individual pet:
805+ System . out. print(
806+ pet. getClass(). getSimpleName() + " " );
807+ if (pet instanceof Pet )
808+ counter. count(" Pet" );
809+ if (pet instanceof Dog )
810+ counter. count(" Dog" );
811+ if (pet instanceof Mutt )
812+ counter. count(" Mutt" );
813+ if (pet instanceof Pug )
814+ counter. count(" Pug" );
815+ if (pet instanceof Cat )
816+ counter. count(" Cat" );
817+ if (pet instanceof EgyptianMau )
818+ counter. count(" EgyptianMau" );
819+ if (pet instanceof Manx )
820+ counter. count(" Manx" );
821+ if (pet instanceof Cymric )
822+ counter. count(" Cymric" );
823+ if (pet instanceof Rodent )
824+ counter. count(" Rodent" );
825+ if (pet instanceof Rat )
826+ counter. count(" Rat" );
827+ if (pet instanceof Mouse )
828+ counter. count(" Mouse" );
829+ if (pet instanceof Hamster )
830+ counter. count(" Hamster" );
831+ }
832+ // Show the counts:
833+ System . out. println();
834+ System . out. println(counter);
835+ }
836+ public static void main (String [] args ) {
837+ countPets(new ForNameCreator ());
838+ }
839+ }
840+ /* Output:
841+ Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat
842+ EgyptianMau Hamster EgyptianMau Mutt Mutt Cymric Mouse
843+ Pug Mouse Cymric
844+ {EgyptianMau=2, Pug=3, Rat=2, Cymric=5, Mouse=2, Cat=9,
845+ Manx=7, Rodent=5, Mutt=3, Dog=6, Pet=20, Hamster=1}
846+ */
847+ ```
848+
849+ 在 ` countPets() ` 中,一个简短的静态方法 ` Pets.array() ` 生产出了一个随机动物的集合。每个 ` Pet ` 都被 ` instanceof ` 检测到并数了一遍。
850+
851+ ` instanceof ` 有一个严格的限制:只可以将它与命名类型进行比较,而不能与 ` Class ` 对象作比较。在前面的例子中,你可能会觉得写出一大堆 ` instanceof ` 表达式很乏味,事实也是如此。但是,也没有办法让 ` instanceof ` 聪明起来,让它能够自动地创建一个 ` Class ` 对象的数组,然后将目标与这个数组中的对象逐一进行比较(稍后会看到一种替代方案)。其实这并不是那么大的限制,如果你在程序中写了大量的 ` instanceof ` ,那就说明你的设计可能存在瑕疵。
852+
555853
556854## 类型转换检测
557855
0 commit comments