Java中的继承与组合
本文主要说明Java中继承与组合的概念,以及它们之间的联系与区别。首先文章会给出一小段代码示例,用于展示到底什么是继承。然后演示如何通过“组合”来改进这种继承的设计机制。最后总结这两者的应用场景,即到底应该选择继承还是组合。
1、继承
假设我们有一个名为Insect(昆虫)的类,这个类包含两个方法:1)移动move(); 2)攻击attack()。
代码如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
class Insect { private int size; private String color; public Insect(int size, String color) { this.size = size; this.color = color; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public void move() { System.out.println("Move"); } public void attack() { move(); //假设昆虫在攻击前必须要先移动一次 System.out.println("Attack"); }} |
现在,你想要定义一个名为Bee(蜜蜂)的类。Bee(蜜蜂)是Insect(昆虫)的一种,但实现了不同于Insect(昆虫)的attack()和move方法。这时候我们可以用继承的设计机制来实现Bee类,就像下面的代码一样:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class Bee extends Insect { public Bee(int size, String color) { super(size, color); } public void move() { System.out.println("Fly"); } public void attack() { move(); super.attack(); }} |
|
1
2
3
4
5
6
|
public class InheritanceVSComposition { public static void main(String[] args) { Insect i = new Bee(1, "red"); i.attack(); }} |
InheritanceVSComposition作为一个测试类,在其main方法中生成了一个Bee类的实例,并赋值给Insect类型的引用变量
i。所以调用i的attack方法时,对应的是Bee类实例的attack方法,也就是调用了Bee类的attack方法。
类的继承结构图如下,非常简单:

输出:
|
1
2
3
|
FlyFlyAttack |
Fly被打印了两次,也就是说move方法被调用了两次。但按理来讲,move方法只应当被调用一次,因为无论是昆虫还是蜜蜂,一次攻击前只移动一次。
问题出在子类(即Bee类)的attack方法的重载代码中,也就是super.attack()这一句。因为在父类(即Insect类)中,调用attack方法时会先调用move方法,所以当子类(Bee)调用super.attack()时,相当于也同时调用了被重载的move方法(注意是子类被重载的move方法,而不是父类的move方法)。
为了解决这个问题,我们可以采取以下办法:
- 删
除子类的attack方法。这么做会使得子类的attack方法的实现完全依赖于父类对于该方法的实现(因为子类继承了父类的attack方法)。如果父类的attack方法不受控制而产生了变更。比如说,父类的attack方法中调用了另外的move方法,那么子类的attack方法也会产生相应的变化,这是一种很糟糕的封装。 - 也可以重写子类的attack方法,像下面这样:
|
1
2
3
4
|
public void attack() { move(); System.out.println("Attack");} |
这样保证了结果的正确性,因为子类的attack方法不再依赖于父类。但是,子类attack方法的代码与父类产生了重复(重复的attack方法会使得很多事情变得复杂,不仅仅是多打印了一条输出语句)。所以第二种办法也不行,它不符合软件工程中关于重用的思想。
如此看来,继承机制是有缺点的:子类依赖于父类的实现细节,如果父类产生了变更,子类的后果将不堪设想。
2、组合
在上面的例子中,可以用组合的机制来替代继承。我们先看一下运用组合如何实现。
attack这一功能不再是一个方法,而是被抽象为一个接口。
|
1
2
3
4
|
interface Attack { public void move(); public void attack();} |
通过对Attack接口的实现,就可以在实现类当中定义不同类型的attack。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class AttackImpl implements Attack { private String move; private String attack; public AttackImpl(String move, String attack) { this.move = move; this.attack = attack; } @Override public void move() { System.out.println(move); } @Override public void attack() { move(); System.out.println(attack); }} |
因为attack功能已经被抽象为一个接口,所以Insect类不再需要有attack方法。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class Insect { private int size; private String color; public Insect(int size, String color) { this.size = size; this.color = color; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public String getColor() { return color; } public void setColor(String color) { this.color = color; }} |
Bee类一种Insect类,它具有attack的功能,所以它实现了attack接口:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// 这个封装类封装了一个Attack类型的对象class Bee extends Insect implements Attack { private Attack attack; public Bee(int size, String color, Attack attack) { super(size, color); this.attack = attack; } public void move() { attack.move(); } public void attack() { attack.attack(); }} |
类图:

测试类代码,将AttackImpl的实例作为Attack类型的参数传给Bee类的构造函数:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
public class InheritanceVSComposition2 { public static void main(String[] args) { Bee a = new Bee(1, "black", new AttackImpl("fly", "move")); a.attack(); // if you need another implementation of move() // there is no need to change Insect, we can quickly use new method to attack Bee b = new Bee(1, "black", new AttackImpl("fly", "sting")); b.attack(); }} |
|
1
2
3
4
|
flymoveflysting |
3、什么时候该用继承,什么时候该用组合?
以下两条原则说明了应该如何选择继承与组合:
- 如果存在一种IS-A的关系(比如Bee“是一个”Insect),并且一个类需要向另一个类暴露所有的方法接口,那么更应该用继承的机制。
- 如果存在一种HAS-A的关系(比如Bee“有一个”attack功能),那么更应该运用组合。
总结来说,继承和组合都有他们的用处。只有充分理解各对象和功能之间的关系,才能充分发挥这两种机制各自的优点。
文章出处:http://www.lupaworld.com/article-242748-1.html
Java中的继承与组合的更多相关文章
- 关于Java中的继承和组合的一个错误使用的例子
[TOC] 关于Java中的继承和组合的一个错误使用的例子 相信绝大多数人都比较熟悉Java中的「继承」和「组合」这两个东西,本篇文章就主要就这两个话题谈论一下.如果我某些地方写的不对,或者比较幼稚, ...
- <Java中的继承和组合之间的联系和区别>
//Java中的继承和组合之间的联系和区别 //本例是继承 class Animal { private void beat() { System.out.println("心胀跳动...& ...
- Java中的继承与组合(转载)
本文主要说明Java中继承与组合的概念,以及它们之间的联系与区别.首先文章会给出一小段代码示例,用于展示到底什么是继承.然后演示如何通过“组合”来改进这种继承的设计机制.最后总结这两者的应用场景,即到 ...
- [译]Java中的继承 VS 组合
(文章翻译自Inheritance vs. Composition in Java) 这篇文章阐述了Java中继承和组合的概念.它首先给出了一个继承的例子然后指出怎么通过组合来提高继承的设计.最后总结 ...
- 菜鸟译文(一)——Java中的继承和组合
阅读英文的能力对于程序员来说,是很重要的.这几年也一直在学习英文,今天心血来潮,就在网上找了一篇简短的博文翻译一下.水平一般,能力有限,还请各位看官多多指点. 译文: 本文将会举例说明Java中继承和 ...
- java中的继承与oc中的继承的区别
为什么要使用继承? 继承的好处: (1)抽取出了重复的代码,使代码更加灵活 (2)建立了类和类之间的联系 继承的缺点: 耦合性太强 OC中的继承 1.OC中不允许子类和父类拥有相同名称的成员变量名:( ...
- Java中的继承
我们在以前的学习中,我们会了C#中的继承,今天我们来了解了解Java中的继承,其实都大同小异啦! 1.语法 修饰符 SubClass extends SuperClass(){ //类定义部分 } e ...
- extends:类似于java中的继承特征,extends="struts-default"
extends:类似于java中的继承特征,extends="struts-default"就是继承struts-default.xml,它里面定义了许多跳转类型.拦截器等一些常用 ...
- 关于java中的继承
我们都知道Java中的继承是复用代码.扩展子类的一种方式,继承使得Java中重复的代码能够被提取出来供子类共用,对于Java程序的性能以及修改和扩展有很大的意义,所以这是一个非常重要的知识点. 那么对 ...
随机推荐
- C语言基础课程 第二课 HelloWorld不为菜鸟所知的秘密
1 愉快的开端hello world 4 1.1 include头文件包含 4 1.2 main函数 4 1.3 注释 4 1.4 {}括号,程序 ...
- 字符串(后缀数组||SAM):NOI2015 品酒大会
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAByIAAALuCAIAAABtq0bwAAAgAElEQVR4nOy9f2wb150vev4boESeln ...
- 数据结构(线段树):BZOJ 1103 [POI2007]大都市meg
1103: [POI2007]大都市meg Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1791 Solved: 925[Submit][Stat ...
- bzoj1251 序列终结者(Splay Tree+懒惰标记)
Description 网上有许多题,就是给定一个序列,要你支持几种操作:A.B.C.D.一看另一道题,又是一个序列 要支持几种操作:D.C.B.A.尤其是我们这里的某人,出模拟试题,居然还出了一道这 ...
- [Sequence Alignment Methods] Dynamic time warping (DTW)
本系列介绍几种序列对齐方法,包括Dynamic time warping (DTW),Smith–Waterman algorithm,Cross-recurrence plot Dynamic ti ...
- LeetCode (85): Maximal Rectangle [含84题分析]
链接: https://leetcode.com/problems/maximal-rectangle/ [描述] Given a 2D binary matrix filled with '0's ...
- hdu4696 想法题
就像1.2元人民币可以凑成任意你想要的面值一样.由于一定会有环,只要有C[i] == 1 就可以造成任何数.够坑吧 #include <cstdio> #include <cstri ...
- Girls' research - HDU 3294 (Manacher处理回文串)
题目大意:给以一个字符串,求出来这个字符串的最长回文串,不过这个字符串不是原串,而是转换过的,转换的原则就是先给一个字符 例如 'b' 意思就是字符把字符b转换成字符 a,那么c->b, d-& ...
- [置顶] 【Git入门之十四】Git GUI
原创作品,转载请标明:http://blog.csdn.net/jackystudio/article/details/12374495 文章都快写完了,Git GUI才浮出水面,很多人要骂我,这么方 ...
- Geometric Shapes (poj3449多边形相交)
题意:给你一些多边形的点,判断每个多边形和那些多边形相交,编号按照字典序输出 思路:枚举每个多边形的每条边看是否相交,这里的相交是包括端点的,关键是给你正方形不相邻两个点求另外两个点怎么求,长方形给你 ...