1.1概述
运行时类型识别,就是在程序运行时,动态地识别对象和类的信息。例如,从容器中获取了一个对象,那么判断这个对象所属的类的过程,就是类型识别的过程。
考虑如下这个问题:公司现在要发奖金,但是不同职位的人获得的奖金不同,把公司员工对象全放到List容器中,从容器中一个一个取出员工,并且向他发放工资。公司管理系统人员组织如图:
如果能对获取的对象进行类型识别,那么问题就解决了。但是类型识别的意义不仅仅在于此,还有类型转换、泛型约束等都有应用。
1.2 如何进行运行时类型识别
1.2.1. 关键字 instanceof
instanceof是Java的一个二元关键字,表示某个对象是否是指定类的实例。如果想知道employee是不是Employee类的一个实例,就可以通过instanceof,语法:employee instanceof Employee。表达式返回一个boolean值 。实例如下:
public abstract class Employee {
protected int salary=0;
public void addSalary(int amount){
this.salary+=amount;
}
public abstract String toString();
}
public class Manager extends Employee{
@Override
public String toString(){
return "Manger's salary: "+this.salary;
}
}
public class Worker extends Employee{
@Override
public String toString(){
return "Worker's salary: "+this.salary;
}
}
public class Company {
//随机生成一个Employee列表,包括Manager和Worker
public static List<Employee> getEmployees(){
List<Employee> employees=new ArrayList<Employee>();
Random random=new Random();
for (int i=0;i<5;i++){
if (random.nextInt(5)>3)
employees.add(new Manager());
else
employees.add(new Worker());
}
return employees;
}
}
public class Bootstrap {
public static void main(String[] args) {
List<Employee> employees=Company.getEmployees();
for (Employee employee:employees){
if (employee instanceof Manager){
employee.addSalary(5000);
}
else
employee.addSalary(1000);
System.out.println(employee.toString());
}
}
}
运行结果:
Worker's salary: 1000
Manger's salary: 5000
Worker's salary: 1000
Manger's salary: 5000
Worker's salary: 1000
上面的代码定义了五个类,其中Worker和Manager是Employee的子类,在Employee类中定义了两个方法,一个是addSalary(int amount)用于增加员工工资,另一个是抽象的toString()方法.Company类声明了一个获取员工列表的方法,Bootstrap类中,首先获取员工列表,接着一个一个把员工取出,并判断员工类型。
考虑这样一个问题,现有一个Manger实例的manger,那么manager是Employee吗?当然,即使是经理也是员工。
1.2.2 Class.isInstance()
现有这样一个问题:员工对象被封存在一个List容器之中,要统计每类员工的人数。因为公司经常有人员变动,所以员工类型经常变化。如果仍使用instanceof关键字,那么每次员工类型变动都要重新写一遍统计代码,很繁琐且扩展性不足。每个Class对象,都有一个方法isInstance(),这个方法用于判断指定对象是不是类的一个实例。这样可以解决上述问题了,用一个Map存放员工类型以及员工数量。初始时数量为0.由于每个类型都有一个isInstance()方法,那么只要把具体的员工对象传给它,让Map进行判断和统计。以后员工类型有变动,只需要修改员工类型Map即可。
public class Counter {
public static Map<Class,Integer> employeeTypes=new HashMap<>();
public static void count(Employee employee){
for (Class clazz: employeeTypes.keySet()){//Set keySet():获取集合中所有键的集合
if (clazz.isInstance(employee)){
int acount=employeeTypes.get(clazz)+1;
employeeTypes.put(clazz,acount);
}
}
}
public static void addEmployeeType(Class clazz){
employeeTypes.put(clazz,0);
}
public static void removeEmployeeType(Class clazz){
employeeTypes.remove(clazz);
}
}
Counter类负责统计各种类型员工的类型,可以通过此类来增加或移除员工类型达到修改的目的。员工的类型以及人数存储在一个Map<Class,Integer>中。当传入一个Employee类时,统计方法首先在键值表中找到该种类型的员工,然后修改员工数量。
public class Bootstrap {
public static void main(String[] args) {
List<Employee> employees= Company.getEmployees();
Counter.addEmployeeType(Manager.class);
Counter.addEmployeeType(Worker.class);
for (Employee employee:employees)
Counter.count(employee);
for (Class clazz:Counter.employeeTypes.keySet()){
System.out.println(clazz.getCanonicalName()+":"+Counter.employeeTypes.get(clazz));
}
}
}
运行结果:
practice.Chapter3.Section1.Worker:4
practice.Chapter3.Section1.Manager:1
上例首先从公司获取了员工列表,然后在统计器中加入两种员工类型,Manager和Worker,让统计器统计这两类员工的数量,接着把员工一个一个的传给统计器,最后打印出统计结果,员工4人,管理者1人