类型信息(3):类型转换前检查(上)

    迄今为止,我们已知的RTTI形式包括:

  1. 传统的类型转换,如“(Shape)”,由RTTI确保类型转换的正确性,如果执行了一个错误的类型转换,就会抛出一个ClassCaseException。
  2. 代表对象的类型的Class对象。通过查询Class对象可以获取运行时所需的信息。

    在C++中,经典的类型转换“(Shape)”并不使用RTTI。它只是简单地告诉编译器将这个对象作为新的类型对待。而java要执行类型检查,这通常被称为“类型安全的向下转型”。之所以叫“向下转型”,是由于类层次结构图从来就是这么排列的。如果将Circle类转换为Shape类型被称作向上转型,那么将Shape转型为Circle,就被称为向下转型。但是,由于知道Circle肯定是一个Shape,所以编译器允许自由地做向上转型的赋值操作,而不需要任何显式的转型操作。编译器无法知道对于给定的Shape到底是什么Shape——它可能就是Shape,或者是Shape的子类型,例如Circle、Square、Traingle或某种其他的类型。在编译期。编译器只能知道它是Shape。因此,如果不使用显式的类型转换,编译器就不允许你执行向下转型赋值,以告知编译器你拥有额外的信息,这些信息使你知道该类型是某种特定类型(编译器将检查向下转型是否合理,因此它不允许向下转型到实际上不是待转型类的子类的类型上)。

    RTTI在java中还有第三种形式,就是关键字instanceof。它返回一个布尔值,告诉我们对象是不是某个特定类型的实例。可以用提问的方式使用它,就像这样:

if(x instanceof Dog)
    ((Dog)x).bark();

    在将x转型成一个Dog前,上面的if语句会检查对象x是否从属于Dog类。进行向下转型前,如果没有其他信息可以告诉你这个对像是什么类型,那么使用instanceof是非常重要的,否则会得到一个ClassCastException异常。

    一般,可能想要查找某种类型(比如要找三角形,并填充成紫色),这时可以轻松地使用instanceof来计数所有对象。例如,假设你有一个类的继承体系,描述了Pet(以及他们的主人,这是在后面的示例中出现的一个非常方便的特性)。这个继承体系中的每个Individual都有一个id和一个可选名字。  尽管下面的类都继承自Individual,但是Individual类复杂性较高,此处并不需要去了解Individual的代码——你只需要了解你可以创建其具名或不具名的对象,并且每个Individual都有一个id()方法,可以返回其唯一的标识符(通过对每个对象计数而创建的)。还有一个toString()方法,如果你没有为Individual提供名字,toString()方法只产生类型名字。

/**
 * 个别的
 */
public class Individual implements Comparable<Individual> {
	private static long counter = 0;
	private final long id = counter++;
	private String name;

	public Individual(String name) {
		this.name = name;
	}

	public Individual() {
	}

	public String toString() {
		return getClass().getSimpleName() + (name == null ? "" : "" + name);
	}

	public long id() {
		return id;
	}

	public boolean equals(Object o) {
		return o instanceof Individual && id == ((Individual) o).id;
	}

	public int hashCode() {
		int result = 17;
		if (name != null)
			result = 37 * result + name.hashCode();
		result = 37 * result + (int) id;
		return result;
	}

	@Override
	public int compareTo(Individual arg) {
		String first = getClass().getSimpleName();
		String argFirst = arg.getClass().getSimpleName();
		int firstCompare = first.compareTo(argFirst);
		if (firstCompare != 0)
			return firstCompare;
		if (name != null && arg.name != null) {
			int secondCompare = name.compareTo(arg.name);
			if (secondCompare != 0)
				return secondCompare;
		}
		return (arg.id < id ? -1 : (arg.id == id ? 0 : 1));
	}
}
/**
 * 人
 */
public class Person extends Individual {
	public Person(String name) {
		super(name);
	}
}
/**
 * 宠物
 */
public class Pet extends Individual {
	public Pet(String name) {
		super(name);
	}

	public Pet() {
	}
}
/**
 * 狗
 */
public class Dog extends Pet {
	public Dog(String name) {
		super(name);
	}

	public Dog() {
		super();
	}
}
/**
 * 杂种狗
 */
public class Mutt extends Dog {
	public Mutt(String name) {
		super(name);
	}

	public Mutt() {
		super();
	}
}
/**
 * 哈巴狗
 */
public class Pug extends Dog {
	public Pug(String name) {
		super(name);
	}

	public Pug() {
		super();
	}
}
/**
 * 猫
 */
public class Cat extends Pet {
	public Cat(String name) {
		super(name);
	}

	public Cat() {
		super();
	}
}
/**
 * 埃及猫
 */
public class EgyptianMau extends Cat {
	public EgyptianMau(String name) {
		super(name);
	}

	public EgyptianMau() {
		super();
	}
}
/**
 * 马恩岛猫
 */
public class Manx extends Cat {
	public Manx(String name) {
		super(name);
	}

	public Manx() {
		super();
	}
}
/**
 * 威尔士猫
 */
public class Cymric extends Manx {
	public Cymric(String name) {
		super(name);
	}

	public Cymric() {
		super();
	}
}
/**
 * 啮齿目动物
 */
public class Rodent extends Pet {
	public Rodent(String name) {
		super(name);
	}

	public Rodent() {
		super();
	}
}
/**
 * 鼠
 */
public class Rat extends Rodent {
	public Rat(String name) {
		super(name);
	}

	public Rat() {
		super();
	}
}
/**
 * 老鼠
 */
public class Mouse extends Rodent {
	public Mouse(String name) {
		super(name);
	}

	public Mouse() {
		super();
	}
}
/**
 * 仓鼠
 */
public class Hamster extends Rodent {
	public Hamster(String name) {
		super(name);
	}

	public Hamster() {
		super();
	}
}

    接下来,我们需要一种方法,通过它可以随机地创建不同类型的宠物,并且为方便起见,还可以创建宠物数组和List。为了使该工具能够适应多种不同的实现,我们将其定义为抽象类:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

public abstract class PetCreator {
	private Random r = new Random();

	public abstract List<Class<? extends Pet>> types();

	public Pet randomPet() {
		int n = r.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 < size; i++)
			result[i] = randomPet();
		return result;
	}

	public ArrayList<Pet> arrayList(int size) {
		ArrayList<Pet> result = new ArrayList<>();
		Collections.addAll(result, createArray(size));
		return result;
	}
}

    抽象的getType()方法在导出类中实现,以获取由Class对象构成的List(这是模版方法设计模式的一种变体)。注意,其中类的类型被指定为“任何从Pet导出的类”,因此newInstance()不需要转型就可以产生Pet。randomPet()随机地产生List中的索引,并使用被选取的Class对象,通过Class.newInstance()来生成该类的新实例。createArray()方法使用randomPet()来填充数组,而ArrayList()方法使用的则是createArray()。

    在调用newInstance()时,可能会得到两种异常,在紧跟try语句块后面的catch子句中可以看到对它们的处理。异常的名字再次成为了一种对错误类型相对比较有用的解释(IllegalAccessException表示违反了java安全机制,在本例中,表示默认构造器为private的情况)。

    当你导出PetCreator的子类时,唯一所需提供的就是你希望使用randomPet()和其他方法来创建的宠物类型的List。getTypes()方法通常只返回对一个静态List的引用。下面是使用forName()的一个具体实现:

import java.util.ArrayList;
import java.util.List;

public class ForNameCreator extends PetCreator {

	private static List<Class<? extends Pet>> types = new ArrayList<>();

	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")
	private static void loader() {
		try {
			for (String name : typeNames)
				types.add((Class<? extends Pet>) Class.forName(name));
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		}
	}

	static {
		loader();
	}

	@Override
	public List<Class<? extends Pet>> types() {
		return types;
	}
}

    loader()方法用Class.forName()创建了Class对象的List,这可能会产生ClassNotFoundException异常,这么做是有意义的,因为你传递给它的是一个在编译期无法验证的String。由于Pet对象在typeinfo包中,因此必须使用报名来引用这些类。

    为了产生具有实际类型的Class对象的List,必须使用转型,这会产生编译期警告。loader()方法被单独定义,然后被置于一个静态初始化子句中,因为@SuppressWarnings注解不能直接置于静态初始化子句之上。

    为了对Pet进行计数,我们需要一个能跟踪各种不同类型的Pet的数量的工具。Map是此需求的首选,其中键是Pet类型名,而值是保存Pet数量的Integer。通过这种方式,你可以询问:“有多少个Hamster对象?”我们可以使用instanceof来对Pet进行计数:

import java.util.HashMap;

import typeinfo.pets.*;

public class PetCount {
	static class PetCounter extends HashMap<String, Integer> {
		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)) {
			System.out.print(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 Rodent)
				counter.count("Rodent");
			if (pet instanceof Rat)
				counter.count("Rat");
			if (pet instanceof Mouse)
				counter.count("Mouse");
			if (pet instanceof Hamster)
				counter.count("Hamster");
		}
		System.out.println();
		System.out.println(counter);
	}

	public static void main(String[] args) {
		countPets(new ForNameCreator());
	}
}

    在CountPets()中,是使用PetCreator来随机地向数组中填充Pet的。然后使用instanceof对该数组中的每个Pet进行测试和计数。

    对instanceof有比较严格的限制,只可将其与命名类型进行比较,而不能与Class对象作比较。在前面的例子中,可能觉得写出那么一大堆instanceof表达式是很乏味的,的确如此。但是也没有办法让instanceof聪明起来,让它能够自动地创建一个Class对象的数组,然后将目标对象与这这个数组中的对象进行逐一的比较(稍后你会看到一个替代方案)。其实这并非是一种如你想象中那般好的限制,因为渐渐的就会理解,如果程序中编写了许多的instanceof表达式,就说明你的设计可能存在瑕疵。

如果本文对您有很大的帮助,还请点赞关注一下。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

游王子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值