栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

《Java 编程思想》学习笔记 14.3 类型转换前先做检查

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

《Java 编程思想》学习笔记 14.3 类型转换前先做检查

*迄今为止,我们已知的RTTI形式包括:
1)传统的类型转换,如“(Shape)”,由RTTI确保类型转换的正确性,如果执行了一个错误的类型转换,就会抛出一个ClassCastException异常。
2)代表对象的类型的Class对象。通过查询Class对象可以获取运行时所需的信息。
在C++中,经典的类型转换“(Shape)”并不使用RTTI。它只是简单的告诉编译器将这个对象作为新的类型对待。而Java要执行类型检查,这通常被称为“类型安全的向下转型”。之所以叫“向下转型”,是由于类层次结构图从来就是这么排列的。如果将Circle类型转换为Shape类型被称作向上转型,那么将Shape转型为Circle,就被称为向下转型。但是,由于知道Circle肯定是一个Shape,所以编译器允许自由的做向上转型的赋值操作,而不需要任何显式的转型操作。编译器无法知道对于给定的Shape到底是什么Shape——它可能就是Shape,或者是Shape的字类型,例如Circle、Square、Triangle或某种其他的类型。在编译期,编译器只能知道它是Shape。因此,如果不使用显式的类型转换,编译器就不允许你执行向下转型赋值,以告知编译器你拥有额外的信息,这些信息使你知道该类型是某种特定类型(编译器将检查向下转型是否合理,因此它不允许向下转型到实际上不是待转型类的子类的类型上)。
RTTI在Java中,还有第三种形式,就是关键字instanceof。它返回一个布尔值,告诉我们对象是不是某个特定类型的实例。可以用提问的方式使用它,就像这样:
if(x instanceof Dog)
((Dog)x).bark();
在将x转型成一个Dog前,上面的if语句会检查对象x是否从属于Dog类。进行向下转型前,如果没有其他信息可以告诉你这个对象是什么类型,那么使用instanceof是非常重要的,否则会得到一个ClassCastException异常。
一般,可能想要查找某种类型(比如要找三角形,并填充成紫色),这时可以轻松地使用instanceof来计数所有对象。例如,假设你有一个类的继承体系,描述了Pet(以及它们的主人,这是在后面的示例中出现的一个非常方便的特性)。在这个继承体系中的每个Individual都有一个id和一个可选的名字。尽管下面的类都继承自Individual,但是Individual类复杂性较高,因此其代码将放到第17章中进行说明与解释。正如你可以看到的,此处并不需要去了解Individual的代码——你只需了解你可以创建其具名或不具名的对象,并且每个Individual都有一个id()方法,可以返回其唯一的标识符(通过对每个对象计数而创建的)。还有一个toString()方法,如果你没有为Individual提供名字,toString()方法只产生类型名。
下面是继承自Individual的类继承体系:
//: typeinfo/pets/Person.java
package typeinfo.pets;
public class Person extends Individual {
public Person(String name) {super(name);}
}///:~
//:typeinfo/pets/Pet.java
package typeinfo.pets;
public class Pet extends Individual {
public Pet(String name) {super(name);}
public Pet() {super();}
]///:~
//:typeinfo/pets/Dog.java
package typeinfo.pets;
public class Dog extends Pet {
public Dog(String name) {super(name);]
public Dog() {super();}
}///:~
//: typeinfo/pets/Mutt.java
package typeinfo.pets;

public class Mutt extends Dog {
public Mutt(String name) {super(name);}
public Mutt() {super();}
}///:~
//: typeinfo/pets/pug.java
package typeinfo.pets;
public class Pug extends Dog {
public Pug(String name) {super(name);}
public Pug() {super();}
}///:~
//: typeinfo/pets/Cat.java
package typeinfo.pets;
public class Cat extends Pet {
public Cat(String name) {super(name);}
public Cat() {super();}
]///:~
//: typeinfo/pets/EgyptianMau.java
package typeinfo.pets;
public class EgyptianMau extends Cat {
public EgyptianMau(String name) {super(name);}
public EgyptianMau() {super();]
]///:~
//: typeinfo/pets/Manx.java
package typeinfo.pets;
public class Manx extends Cat {
public Manx(String name) {super(name);}
public Manx() {super();}
}///:~
//: typeinfo/pets/Cymric.java
package typeinfo.pets;
public class Cymric extends Manx {
public Cymric(String name) {super(name);}
public Cymric() {super();}
}///:~
//: typeinfo/.pets/Rodent.java
package typeinfo.java
package typeinfo.pets;
public class Rodent extends Pet {
public Rodent(String name) {super(name);}
public Rodent() {super();]
}///:~
//: typeinfo/pets/Rat.java
package typeinfo.pets;
public class Rat extends Rodent {
public Rat(String name) {super(name);}
public Rat() {super();]
}///:~
//:typeinfo/pets/Mouse.java
package typeinfo.pets;
public class Mouse extends Rodent {
public Mouse(String name) {super(name);}
public Mouse() {super();}
} ///:~
//: typeinfo/pets/Hamster.java
package typeinfo.pets;
public class Hamster extends Rodent {
public Hamster(String name) {super(name);}
public Hamster() {super();}
}///:~
接下来,我们需要一种方法,通过它可以随机的创建不同类型的宠物,并且为方便起见,还可以创建宠物数组和List。为了使该工具能够适应多种不同的实现,我们将其定义为抽象类:
//: typeinfo/pets/PetCreator.java
//Creats random sequences of Pets.
package typeinfo.pets;
import java.util.;
public abstract class PetCreator {
private Random rand = new Random(47);
// The List of the different types of Pet to create:
public abstract List> types();
public Pet randomPet() { //create one random Pet
int n = rand.nextInt(types().size());
try{
return types().get(n).newInstance();
}catch(InstantiationException e) {
throw new RuntimeException(e);
}catch(IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public Pet[] createArray(int size) {
Pet[] result = new Pet[size];
for(int i=0;i result[i] = randomPet();
return result;
}
public ArrayList arrayList(int size) {
ArrayList result = new ArrayList();
Collections.addAll(result, createArray(size));
return result;
}
} ///:~
抽象的getTypes()方法在导出类中实现,以获取由Class对象构成的List(这是模版方法设计模式的一种变体)。注意,其中类的类型被指定为“任何从Pet导出的类”,因此newInstance()不需要转型就可以产生Pet。RandomPet()随机的产生List中的索引,并使用被选取的Class对象,通过Class.newInstance()来生成该类的新实例。createArray()方法使用randomPet()来填充数组,而arrayList()方法使用的则是createArray()。
在调用newInstance()时,可能会得到两种异常,在紧跟try语句块后面的catch子句可以看到对它们的处理。异常的名字再次成为了一种对错误类型相对比较有用的解释(IllegalAccess-Exception表示违反了Java安全机制,在本例中,表示默认构造器为private的情况)。
当你导出PetCreator的子类时,唯一所需提供的就是你希望使用randomPet()和其他方法来创建的宠物类型的List。getTypes()方法通常只返回对一个静态List的引用。下面是使用forName()的一个具体实现:
//: typeinfo/pets/ForNameCreator.java
package typeinfo.pets;
import java.util.
;
public class ForNameCreator extends PetCreator {
private static List> types = new ArrayList>();
//Types that you want to be randomly created;
private static String[] typeNames = {
“typeinfo.pets.Mutt”,
“typeinfo.pets.Pug”,
“typeinfo.pets.EgyptianMau”,
“typeinfo.pets.Manx”,
“typeinfo.pets.Cymric”,
“typeinfo.pets.Rat”,
“typeinfo.pets.Mouse”,
“typeinfo.pets.Hamster”,
};
@SuppressWarnings(“unchecked”)
privated static void loader() {
try {
for(String name : typeNames)
types.add((Class)Class.forName(name));
}catch(ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
static {loader();}
public List> types() {return types;}
}///:~
loader()方法用Class.forName()创建了Class对象的List,这可能会产生ClassNotFound-Exception异常,这么做是有意义的,因为你传递给它的是一个在编译期无法验证的String。由于Pet对象在typeinfo包中,因此必须使用包名来引用这些类。
为了产生具有实际类型的Class对象的List,必须使用转型,这会产生编译期警告。loader()方法被单独定义,然后被置于一个静态初始化子句中,因为@SuppressWarnings注解不能直接置于静态初始化子句之上。
为了对Pet进行计数,我们需要一个能够跟踪各种不同类型的Pet的数量的工具。Map是此需求的首选,其中键是Pet类型名,而值是保存Pet数量的Integer。通过这种方式,你可以询问:“有多少个Hamster对象?”我们可以使用instenceof来对Pet进行计数:
//: typeinfo/PetCount.java
//Using instanceof.
import typeinfo.pets.;
import java.util.
;
import static net.mindview.util.Print.*;

public class PetCount {
static class PetCounter extends HashMap {
public void count(String type) {
Integer quantity = get(type);
if (quantity == null)
put(type,1);
else
put(type,quantity+1);
}
}
public static void countPets(PetCreator creator) {
PetCounter counter = new PetCounter();
for(Pet pet : creator.createArray(20)) {
//List each individual pet:
printnb(pet.getClass().getSimpleName() +" ");
if(pet instanceof Pet) counter.count(“Pet”);
if(pet instanceof Dog) counter.count(“Dog”);
if(pet instanceof Mutt) counter.count(“Mutt”);
if(pet instanceof Pug) counter.count(“Pug”);
if(pet instanceof Cat) counter.count(“Cat”);
if(pet instanceof Manx) counter.count(“EgyptianMau”);
if(pet instanceof Manx) counter.count(“Manx”);
if(pet instanceof Manx) counter.count(“Cymric”);
if(pet instanceof Rat) counter.count(“Rat”);
if(pet instanceof Mouse) counter.count(“Mouse”);
if(pet instanceof Hamster) counter.count(“Hamster”);
}
//Show the counts:
print();
print(counter);
}
public static voia main(String[] args) {
countPets(new ForNameCreator());
}
}//:~
在CountPets()中,是使用PetCreator来随机地向数组中填充Pet的。然后使用instanceof对该数组中的每个Pet进行测试和计数。
对instanceof有比较严格的限制:只可将其与命名类型进行比较,而不能与Class对象作比较。在前面的例子中,可能觉得写出那么一大堆instanceof表达式是很乏味的,的确如此。但是也没有办法让instanceof聪明起来,让它能够自动地创建一个Class对象的数组,然后将目标对象与这个数组中的对象进行逐一的比较(稍后会看到一个替代方案)。其实这并非是一种如你想象中那般好的限制,因为渐渐的读者就会理解,如果程序中编写了许多的instanceof表达式,就说明你的设计可能存在瑕疵。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/425443.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号