注意如类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。
子类可以有自己的个性子类当然可以有自己的行为和外观了,也就是方法和属性,那这里为什么要再提呢?是因为里氏替换原则可以正着用,但是不能反过来用。在子类出现的地方,父类未必就可以胜任。还是以刚才的关于的例子为例,有几个比较“响亮”的型号,比如AK47、G3狙击等,把这两个型号的引入后的Rifle子类图如图2-4所示。
图2-4 增加AK47和G3后的Rifle子类图
很简单,G3继承了Rifle类,狙击手(Snipper)则直接使用G3狙击,源代码如代码清单2-7所示。
代码清单2-7 G3源码代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class G3 extends Rifle {
//都是携带一个精准的望远镜
public void zoomOut(){
System.out.println('通过望远镜查看敌人...');
}
public void shoot(){
System.out.println('G3射击...');
}
}
有就有狙击手,狙击手类的源代码如代码清单2-8所示。
代码清单2-8 G3狙击手类的源码代码
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
public class Snipper {
public void killEnemy(G3 g3){
//首先看看敌人的情况,别杀死敌人,自己也被人干掉
g3.zoomOut();
//开始射击
g3.shoot();
}
}
狙击手,为什么叫Snipper?Snipe翻译过来就是鹬,就是鹬蚌相争,渔翁得利中的那个动物,英国贵族到印度打猎,发现这个鹬很聪明,人一靠近就飞走了,没办法就开始伪装、远程精准射击,于是乎Snipper就诞生了。
狙击手使用来杀死敌人,业务场景Client类的源代码如代码清单2-9所示。
代码清单2-9 狙击手使用G3杀死敌人
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Client {
public static void main(String[] args) {
//产生三毛这个狙击手
Snipper sanMao = new Snipper();
sanMao.killEnemy(new G3());
}
}
狙击手使用G3杀死敌人,运行结果如下所示:
通过望远镜查看敌人...
G3射击...
在这里系统直接调用了子类,一个狙击手是很依赖的,别说换一个型号的了,就是换一个同型号的也会影响射击,所以这里就直接把子类传递了进来。这个时候,我们能不能直接使用父类传递进来呢?修改一下Client类,如代码清单2-10所示。
代码清单2-10 使用父类作为参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {
public static void main(String[] args) {
//产生三毛这个狙击手
Snipper sanMao = new Snipper();
Rifle rifle = new Rifle();
sanMao.killEnemy((G3)rifle);
}
}
显示是不行的,会在运行期抛出java.lang.ClassCastException异常,这也是大家经常说的向下转型(downcast)是不安全的,从里氏替换原则来看,就是有子类出现的地方父类未必就可以出现。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-21726-3.html
就是经济下滑加快