(转载) Java子类与父类之间的对象转换
在使用Java的多态机制时,常常使用的一个特性便是子类和父类之间的对象转换。从子类向父类的转换称为向上转换(upcasting),通过向上转换,我们能够在编写程序时采用通用程序设计的思想,在需要使用子类对象的时候,通过把变量定义为父类型,我们可以通过一个变量,使用该父类型的所有子类型实例;从父类型向子类型的转换称为向下转换(downcasting),通过向下转换,我们能在必要的时候,将父类型变量转换成子类型变量,使用一些通过子类型才能够使用的方法。以下是我对于对象转换的一些个人理解,如有不对,欢迎指正,虚心向大神们请教。
首先是从子类向父类的向上转换。向上转换比较直观,总是能够将一个子类的实例转换为一个父类的对象,从继承链的角度,这个特性很容易理解:继承是一种“是一种”的关系,从父类派生出的子类,我们都能理解为,子类总是父类的一个实例。比如说,Fruit类派生出了Orange类,Apple类,Orange和Apple都是Fruit;Animal类派生出了Tiger类和Lion类,Tiger和Lion都是Animal。因此,从子类向父类的转换不需要什么限制,只需直接将子类实例赋值给父类变量即可,这也是Java中的多态的实现机制。
//Test.java
public class Test {
public static void main(String args[]) {
Animal tiger = new Tiger();
Animal lion = new Lion();
}
} class Animal {
String name;
Animal() {
name = "animal";
}
Animal(String name) {
this.name = name;
}
}
class Tiger extends Animal {
Tiger() {
super("tiger");
}
}
class Lion extends Animal {
Lion() {
super("lion");
}
}
然而从父类向子类的向下转换就稍微复杂一些了。在讲述向下转换之前,也许有些刚学java的朋友会有点不解为什么要使用向下转换,使用多态和动态绑定机制通过父类型变量使用子变量不就可以了么(比如我就曾对此感到疑惑)。这就要考虑到,在继承关系中,有一些方法是不适合由父类定义并由子类继承并重写的,有些方法是子类特有的,不应该通过继承得到,且子类可能也会有自己特有的成员变量,那么在使用多态机制的时候,若我们要通过父类型变量使用到这些子类特有的方法和属性的话,就需要将服类型变量转换成对应的子类型变量。一个典型例子便是标准库中的数据类型包装类:Integer类,Double类,Long类等,它们都继承自Number类,且它们都有一个方法叫做compareTo用于比较两个同样的类型。然而这个方法是这些子类通过实现Comparable接口来实现的,在Number类中并没有该方法的实现,因此若要通过Number类型变量来使用compareTo方法,就要先将Number类转换成子类的对象。
从父类向子类的转换就有限制了。首先,父类变量向子类转换必须通过显式强制类型转换,采取和向上转换相同的直接赋值方式是不行的,;并且,当把一个父类型变量实例转换为子类型变量时,必须确保该父类变量是子类的一个实例,从继承链的角度来理解这些原因:子类一定是父类的一个实例,然而父类却不一定是子类的实例;比如说,Fruit未必是Orange,它可能是Apple;Animal也不一定是Tiger,它可能是Lion。用代码来解释一下:
Animal tiger = new Tiger();
Animal lion = new Lion();
在前面向上转换的代码示例当中,main方法中的这两行代码,意思就是父类型变量tiger是子类Tiger的一个实例,lion是Lion的一个实例。
也就是说,如果要把tiger转换为Tiger类型,必须保证tiger本身是Tiger的一个实例,在上例中,如果要把tiger转换成Lion类型,或是把Lion类型转换为Tiger类型,都是行不通的,在运行时,这会抛出一个运行异常ClassCastException,表示类转换异常。因此,在进行父类向子类的转换时,一个好的习惯是通过instanceof运算符来判断父类变量是否是该子类的一个实例:
Tiger t = null;
if(tiger instanceof Tiger)
t = (Tiger)tiger;
如果要通过父类调用子类变量的方法,那么要注意要将父类型变量和强制转换用括号括起来:
Number i = new Integer(3);
System.out.println(
((Integer)i).compareTo(new Integer(4))
);
因为成员访问运算符.的优先级大于类型转换,所以要用括号括起来保证类型转换先于成员访问进行运算。
前面说到用instanceof判断父类是否是子类的一个实例是一个好习惯,如果不养成这个习惯的话很容易出问题,请看下面这段代码:
Animal animal = new Tiger();
Lion lion = (Lion)tiger;
前面说到,这段代码会在运行时抛出ClassCastException异常,然而,这段代码却是能够编译成功的。原因是因为,Java编译器并没有聪明到能够在编译阶段就知道父类型变量是哪一个子类的实例,所以,将animal转换为Lion类型的代码:(Lion)animal是能够编译通过的,即使事实上我们能看到animal是Tiger的一个实例,因为Animal类型确实能转换成Lion类型,所以这条语句是合法的。所以,如果没有使用instanceof防止不同子类型之间的对象转换,而又不能指望编译器检查出这种转换逻辑错误的话,就很容易犯错了。
(转载) Java子类与父类之间的对象转换的更多相关文章
- Java子类与父类之间的类型转换
1.向上转换 父类的引用变量指向子类变量时,子类对象向父类对象向上转换.从子类向父类的转换不需要什么限制,只需直接蒋子类实例赋值给父类变量即可,这也是Java中多态的实现机制. 2.向下转换 在父类变 ...
- Java拾遗(一):浅析Java子类和父类的实例化顺序 及 陷阱
本文主要介绍Java里经常使用的子类和父类的变量实例化顺序及陷阱,并结合一个Android实例来探讨此问题.日后编程中应尽量避免此陷阱. 首先看以下一段代码: 定义一个虚类Server.java pa ...
- c++ 子类与父类之间的类型转换
子类与父类之间的类型转换 先给一段代码 class Base { public: int a = 10; }; class pub_Derv : public Base { Base *getBase ...
- java 子类重写父类的方法应注意的问题
若想实现一个合格重写方法,而不是重载,那么必须同时满足下面的要求! A.重写规则之一: 重写方法不能比被重写方法限制有更严格的访问级别.(但是可以更广泛,比如父类方法是包访问权限,子类的重写方法 ...
- Java子类和父类的初始化执行顺序
要明白子类和父类的初始化执行顺序,只需要知晓以下三点,就不会再弄错了. 1.创建子类对象时,子类和父类的静态块和构造方法的执行顺序为:父类静态块->子类静态块->父类构造器->子类构 ...
- java 子类、父类中静态代码块、字段,非静态代码块、字段以及构造函数的初始化顺序和次数
一个类中的数据初始化顺序是面试官非常喜欢出的面试题之一,本文用一个实例来介绍java中子类.父类中静态代码块.字段,非静态代码块.字段以及构造函数的执行顺序和次数. 一.包结构
- Java子类对于父类中static方法的继承
今天看到了Java中子类继承父类的一个相关讨论,在网上综合了各家的观点,写下了一篇简短的总结. 问题如下,在父类中含有staic修饰的静态方法,那么子类在继承父类以后可不可以重写父类中的静态方法呢? ...
- Java 子类 instanceof 父类
1.结论: 子类 instanceof 父类 == true 父类 instanceof 子类 == false 2.测试代码 @Test public void testInstanceof() { ...
- Java子类访问父类的私有成员变量
/**子类会继承父类所有的属性和方法. * 但是根据不同的权限标识符,子类不可见父类的私有变量,但可以通过父类的公共方法访问私有变量 * 所以对于重名变量,子类和父类都各有一份. * 对于子类和父类中 ...
随机推荐
- 真正从0开始用Unity3D制作类战地2玩法的类龙之谷、王者荣耀的手游(暨全平台游戏)
如题,(从2017年10月18日开始)正在利用业余时间研发一款神泣Shaiya2手游,引擎用Unity3D. 原因主要有2点: 对神泣太多感情,希望能做点什么来纪念乃至留下神泣这款网游: 时机已到,是 ...
- 开发人员的福音:微软、谷歌、Mozilla将他们所有的web API文档放在同一个地方
Tips 原文作者:Liam Tung 原文地址:Developers rejoice: Microsoft, Google, Mozilla are putting all their web A ...
- angularJS的一点小笔记
AngularJS用法:-------------------------------------------------- AngularJS 指令: AngularJS 通过被称为 指令 的新属性 ...
- kafka 的 createDirectStream
一入大数据深似海,脑袋不够用了,先留下只言片语. kafka api中给出2类直接获取流的接口:createStream和createDirectStream. createStream比较简单,只需 ...
- 典型的NIO代码
public void selector() throws IOException { ByteBuffer buffer = ByteBuffer.allocate(1024); Selector ...
- 记一次mysql千万订单汇总查询优化
公司订单系统每日订单量庞大,有很多表数据超千万.公司SQL优化这块做的很不好,可以说是没有做,所以导致查询很慢. 正题 节选某个功能中的一句SQL EXPLAIN 查看执行计划 EXPLAIN + S ...
- 【Telerik控件学习】-制作3D效果的柱状图(ChartView)
首先,定义柱状图,并设置自定义的DataTemplate <telerik:RadCartesianChart > <telerik:RadCartesianChart.Horizo ...
- 写给想成为前端工程师的同学们 ―前端工程师是做什么的?a
前端工程师是做什么的? 前端工程师是互联网时代软件产品研发中不可缺少的一种专业研发角色.从狭义上讲,前端工程师使用 HTML.CSS.JavaScript 等专业技能和工具将产品UI设计稿实现成网站产 ...
- HDU1754 I hate it(线段树 单点修改)
好久没打线段树,来一道练练手,但说句实话,I really hate it!!!! 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管 ...
- SSH框架整合--applicationContext.xml文件配置实例
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.spr ...