【Java面向对象】5-6 instanceof 关键字和引用类型转换
§5-6 instanceof
关键字和引用类型转换
在上一节,我们了解了 OOP 的第三个特征——多态。本节将围绕多态,了解 instanceof
关键字和引用类型转换。
5-6.1 instanceof
用法
用法:instanceof
是一个二元操作符,用于测试左边的对象是否属于右边类的实例。返回一个布尔值,是则为 true
,否则为 false
。
我们继续以上一节的类 Parent
和 Child
为例。其中,在测试类中,我们有:
//实例一
Object object = new Child();
System.out.println(object instanceof Child);
System.out.println(object instanceof Parent);
System.out.println(object instanceof Object);
System.out.println(object instanceof String);
运行,得到结果:
true
true
true
false
其中,所涉及到的三个类的继承关系满足以下线性关系:Object -> Parent -> Child
。
这时,在原有的继承关系中,我们新建一个 Parent
的子类 Kid
,将原有的继承关系从线性更改为树状结构。在测试类中测试一下代码:
//实例二
Parent parent = new Child();
System.out.println(parent instanceof Child);
System.out.println(parent instanceof Kid);
System.out.println(parent instanceof Parent);
System.out.println(parent instanceof Object);
System.out.println(parent instanceof String);
尝试编译,会发现最后一条语句编译不通过:
将最后一条语句注释掉后,再次运行,得到结果:
true
false
true
true
5-6.2 instanceof
特点与分析
综合上述两个例子来看,我们会发现:
- 当左边对象的引用变量类型与右边的类不存在继承关系时,编译不通过;
- 编译时,编译器值判断数据类型是否符合,因此会比较左边的引用变量类型是否与右边的类具有继承关系;
- 实际运行时,JVM 会将两侧的对象所属类作比较,得出表达式的值;
- 同多态,
instanceof
满足编译看左,运行看右的规则。
运用上述的发现分析上述示例:
编译时,根据多态的 ”编译看左“ 原则,编译器只会将 instanceof
左边引用变量的类型和右边的类类型做比较。若二者存在继承关系,则编译通过,但此时,编译器并不在乎该表达式的值。因此,示例一中引用变量 object
的类型是 Object
,而该类是所有除自己之外的类的父类,因此,编译通过。而实例二中,引用变量 parent
的类型是 Parent
,而该类与 String
并不存在继承关系,因此,编译不通过,触发异常。
运行时,JVM 比较的是对象本身所属的类类型。若 instanceof
左边的引用变量所指对象的类类型(即对象的实际类型)属于右边的类的子类或自己,则返回 true
,否则,左边对象实际类型是右边的类的父类或关系中属于同级关系,则返回 false
。
自 JDK 14 起,instanceof
关键字具有了一种新用法(语法糖),将判断与转换结合在一起:
obj instanceof Bean newObjRef;
当上述的 instanceof
语句的值为 true
时,会将 obj
转换为 Bean
类的对象,名为 newObjRef
;否则则不转换,返回 false
。
5-6.3 引用类型转换
在基础章节中,我们介绍过基本数据类型的类型转换。这里稍作回顾:
- 低级数据类型无需强制转换,可自动转换为高级数据类型,且不会发生精度、信息丢失;
- 将高级数据类型转换为低级数据类型,则必须使用强制转换,这一过程可能造成精度丢失;
类似地,我们把强制类型转换从基本数据类型扩展到引用数据类型,上述原则仍然成立。
我们继续以上一节的继承关系类为例(Parent
,Child
):
Parent child = new Child();
父类优先级比子类更高,是高级数据类型。因此,由低级转向高级,向上传递,自动进行。
但如果想要通过父类访问子类特有方法,若不执行强制类型转换,根据 ”编译看左“ 的原则,编译器检查时发现父类不存在所需方法便会报错。因此,父类若想访问子类方法,必须向下转型。
子类一定是父类的一个实例(实例化子类时必须先实例化父类),但是父类不一定是子类的实例。子类向上转型为父类时,子类特有的方法将会被舍去;父类强制转换为子类时,将无法补全子类特有的内容。
在使用强制类型转换时,应当注意:
- 只允许父类的引用指向子类的对象;
- 子类转换成父类,向上传递,自动进行;
- 父类转换为子类,向下传递,强制转换;
- 方便方法调用,减少重复代码,实现简洁性;
- 强制转换有风险,使用时还需斟酌。