博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 的上溯造型和下溯造型以及举例,以及判断参数等指向的类
阅读量:4125 次
发布时间:2019-05-25

本文共 3108 字,大约阅读时间需要 10 分钟。

(一)

先给出个例子,代码如下:

/** * @author WangQun * 动物抽象类 */ abstract class Animal { public abstract void speak(); public void eat(){ // 闷头吃,不做额外的事情 } } /** * @author WangQun * 门神接口 */ interface DoorGod { void guard(); } /** * @author WangQun * 猫,继承自动物 */ class Cat extends Animal { @Override public void eat() { try { Thread.sleep( 1000 ); } catch (InterruptedException e) { e.printStackTrace(); } super .eat(); } @Override public void speak() { System.out.println( " 喵喵 " ); } } /** * @author WangQun * 狗,继承自动物,实现门神接口 */ class Dog extends Animal implements DoorGod{ @Override public void speak() { System.out.println( " 汪汪 " ); } public void guard() { while ( true ){ System.out.println( " 汪汪 " ); } } }

其中Animal为基类,定义speakeat方法,eat方法给出了空实现; DoorGod为门神接口,定义了 guard方法来守护家门; Cat为继承Animal的子类,这里假定猫有挑食的习惯,在eat中要耽搁点时间看看伙食;Dog也为继承Animal的子类,同时它实现了DoorGod接口来守护家门。

先说说上溯造型(upcasting)。这个术语缘于继承关系图的传统画法:将基类至于顶部,而向下发展的就是派生类。根据上面的sample,我给出下面的一个小应用:

public class Main { /** * @param animal * 上溯,传入的参数强制转换成其父类 */ public static void upcasting(Animal animal){ animal.speak(); animal.eat(); } public static void main(String[] args) { Animal dog1 = new Dog(); upcasting(dog1); Dog dog2 = new Dog(); upcasting(dog2); } }

由于upcasting(Animal animal)方法的参数是 Animal类型的,因此如果传入的参数是 Animal的子类,传入的参数就会被转换成父类Animal类型,这样你创建的Dog对象能使用的方法只是Animal中的签名方法;也就是说,在上溯的过程中,Dog的接口变窄了,它本身的一些方法(例如实现了 DoorGodguard方法)就不可见了。如果你想使用Dog中存在而Animal中不存在的方法(比如guard方法),编译时不能通过的。由此可见,上溯造型是安全的类型转换。另一方面,虽然upcasting(Animal animal)方法的参数是 Animal类型,但传入的参数可以是Animal的派生类(这也是OO编程中惯用的编程方法),这里面就有个对象的类型识别问题,也就是运行时类型识别(run-time type identification,缩写为RTTI) ,这也可以单独写一篇文章了,《Thinking in Java》中的第10章详细地阐述了RTTI

相对于类型转换安全的上溯造型,下溯造型就未必是安全的了。我们经常会做些强制类型转换的事情,有时我们也会无意间遇到 ClassCastException的转换异常(从这一点来说,我们应该多用范型来避免不安全的类型转换)。例如:

public static void downcasting(Animal animal){ //判断类是不是实现自哪个接口 if(animal instanceof DoorGod){ DoorGod doorGod = (DoorGod)animal; doorGod.guard(); } if(animal instanceof Cat){ Cat cat = (Cat)animal; cat.speak(); } }

如果没有采取措施(上面使用的措施是instanceof)判断对象的类型,那么向下的强制转换就是不安全的。这种转换错误在编译时是不能检测出来的,只有在运行时才会抛出 ClassCastException异常,对于测试来说,这样的错误也是很难检测的。

总的来说,上溯造型与下溯造型概念上还是很简单的。但即便最简单的东西,我们也有可能犯下错误。用了那么多的框架后,回头看看基础知识,发现自己的根基并不是想象中的牢固阿。

注:本文参考了Thinking in Java》,这本书真需要细细品味啊!

(二)

当你拿到一个对象的引用时(例如参数),你可能需要判断这个引用真正指向的类。所以你需要从该类继承树的最底层开始,使用instanceof操作符判断,第一个结果为true的类即为引用真正指向的类。

例如下面的例子:

class Person{} class Student extends Person{} class Postgraduate extends Student{} class Animal{} public class InstanceofTester { public static void main(String[] args) { instanceofTest(new Student()); } public static void instanceofTest(Person p){ // 判断p的真正类型 if (p instanceof Postgraduate){ System.out.println("p是类Postgraduate的实例"); } else if(p instanceof Student){ System.out.println("p是类Student的实例"); } else if(p instanceof Person){ System.out.println("p是类Person的实例"); } else if(p instanceof Object) { System.out.println("p是类Object的实例"); } /*if(p instanceof Animal){//此错编译错误,所以做注释 System.out.println("p是类Animal的实例"); }*/ } }

这个程序的输出结果是:p是类Student的实例

Person类所在的继承树是:Object<--Person<--Student<--Postgraduate。
这个例子中还加入一个Animal类,它不是在Person类的继承树中,所以不能作为instanceof的右操作数。
你可以跑跑程序,应该就明白什么意思了。

转载地址:http://btlpi.baihongyu.com/

你可能感兴趣的文章
字符设备驱动程序快速参考
查看>>
Linux并发控制
查看>>
RGB颜色对照表
查看>>
Linux下编译器搜索头文件路径
查看>>
Linux驱动ioctl的使用
查看>>
C语言结构体末端定义空数组
查看>>
gdb调试的基本命令
查看>>
利用socket raw抓包
查看>>
NetLink详解
查看>>
利用libpcap抓包(一)----------libpcap基本使用
查看>>
利用libpcap抓包(二)----------网络数据包头结构定义
查看>>
利用libpcap抓包(三)----------数据包解析函数的实现
查看>>
利用libpcap抓包(四)----------抓包主函数的实现
查看>>
利用netfilter抓包(一)----------netfilter介绍
查看>>
利用netfilter抓包(二)----------抓包函数的实现
查看>>
C字符串分析(使用GDB调试进行地址分析)
查看>>
重拾Python学习(一)----------Python基础
查看>>
重拾Python学习(二)----------函数
查看>>
重拾Python学习(三)----------高级特性
查看>>
重拾Python学习(四)----------函数式编程
查看>>