[译]Java中的继承 VS 组合
(文章翻译自Inheritance vs. Composition in Java)
这篇文章阐述了Java中继承和组合的概念。它首先给出了一个继承的例子然后指出怎么通过组合来提高继承的设计。最后总结出怎么在两者中作出选择。
1.继承
现在假设我们有一个Insect类。这个类包含了两个方法:1) move() and 2) attack().
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(); //assuming an insect needs to move before attacking
System.out.println("Attack");
}
}
现在你想定义一个Insect类型的Bee类,但是对于方法attack() 和move()有不同的实现。这个可以向下面这样通过继承设计来先实现:
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();
}
}
public class InheritanceVSComposition {
public static void main(String[] args) {
Insect i = new Bee(1, "red");
i.attack();
}
}
类的继承图就像下面这么简单:

输出:
Fly
Fly
Attack
Fly被打印出了两次,这就表明move()方法被调用了两次。但是它只应该被调用一次。这个问题是因为super.attack()方法引起的。Insect的attack()方法调用了move()方法。当一个子类调用super.attack()方法的时候,它也会调用覆盖的move()方法。
为了修复这个问题,我们可以:
1.消除子类的attack()方法。这个将使子类依赖超类的attack()的实现。如果在超类中的attack()方法在后来被改变了,例如超类的attack()方法使用另外一个方法去移动,那么子类也需要去改变了。这是一个不好的设计。
2.像下面这样重写attack()方法:
public void attack() {
move();
System.out.println("Attack");
}
这将会保证结果的正确性,因为子类将不再依赖超类了。但是,代码重复了超类,这个不符合软件工程中复用的规则。
这个继承设计是不好的,因为子类依赖超类的实现。如果超类改变了那么结果就会发生改变。
2.组合
组合可以代替继承在这个场景中使用。让我们来看下组合的解决方式。
attack()方法作为接口中一个抽象的方法。
interface Attack {
public void move();
public void attack();
}
不同种类的attack可以用实现Attack接口而被重新定义。
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有任何关系了。
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;
}
}
Beeb是Insect类型,它可以有attack方法>
// This wrapper class wrap an Attack object
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();
}
}
类的结构图:

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();
}
}
fly
move
fly
sting
3.什么时候应该选择哪种方法呢?
下面两个场景可以指导在继承和组合总作出选择:
1.如果是IS-A关系,而且一个类想把它的所有接口暴露给另外一个类,那么应该选择继承。
2.如果是HAS-A关系,那么应该选择组合。
总而言之,继承和组要都有他们的用途,需要了解他们相对的优势。
[译]Java中的继承 VS 组合的更多相关文章
- 关于Java中的继承和组合的一个错误使用的例子
[TOC] 关于Java中的继承和组合的一个错误使用的例子 相信绝大多数人都比较熟悉Java中的「继承」和「组合」这两个东西,本篇文章就主要就这两个话题谈论一下.如果我某些地方写的不对,或者比较幼稚, ...
- <Java中的继承和组合之间的联系和区别>
//Java中的继承和组合之间的联系和区别 //本例是继承 class Animal { private void beat() { System.out.println("心胀跳动...& ...
- Java中的继承与组合(转载)
本文主要说明Java中继承与组合的概念,以及它们之间的联系与区别.首先文章会给出一小段代码示例,用于展示到底什么是继承.然后演示如何通过“组合”来改进这种继承的设计机制.最后总结这两者的应用场景,即到 ...
- 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程序的性能以及修改和扩展有很大的意义,所以这是一个非常重要的知识点. 那么对 ...
随机推荐
- ABP项目中的使用AutoMapper
AutoMapper之ABP项目中的使用 最近在研究ABP项目,昨天写了Castle Windsor常用介绍以及其在ABP项目的应用介绍 欢迎各位拍砖,有关ABP的介绍请看阳光铭睿 博客 AutoMa ...
- Java魔法堂:注解用法详解——@SuppressWarnings(转)
一.前言 编码时我们总会发现如下变量未被使用的警告提示: 上述代码编译通过且可以运行,但每行前面的“感叹号”就严重阻碍了我们判断该行是否设置的断点了.这时我们可以在方法前添加 @SuppressWar ...
- CIC and Fir 滤波器的级联
在FDATool中 CIC 和 Fir 级联滤波器的设计 1 设计CIC滤波器的幅频特性曲线如下 2.设计FIR 滤波器的幅频特性曲线如下 3.总的特性曲线如下 4.把通带部分放大后的图,比较平坦
- 将Sublime Text 2搭建成一个好用的IDE(转)
将Sublime Text 2搭建成一个好用的IDE 说起编辑器,可能大部分人要推荐的是Vim和Emacs,本人用过Vim,功能确实强大,但是不是很习惯,之前一直有朋友推荐SUblime Text 2 ...
- Serverlet具体解释
Serverlet简单介绍: Servlet(Server Applet),全称Java Servlet,未有中文译文.是用Java编写的server端程序.其主要功能在于交互式地浏览和改动数据,生成 ...
- Saving HDU (贪心)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2111 好久不刷题,拿到水题切了切,,,,,题意刚开始都没有理解,,,,真是弱了,,,, 简单贪心,,, ...
- bigdata_hadoop集群配置_内存分配
haoop集群 做好内存管理跟重要,不然经常会给抛出个 OutMemory ,内存溢出 以horntonworks给出推荐配置为样本,给出一种常见的Hadoop集群上各组件的内存分配方案.配置时 ...
- js中prototype用法(转)
JavaScript能够实现的面向对象的特征有:·公有属性(public field)·公有方法(public Method)·私有属性(private field)·私有方法(private fie ...
- Java设计模式菜鸟系列(四)工厂方法模式建模与实现
转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39760895 工厂方法模式(Factory Method) 工厂方法:顾名思义,就是调用工 ...
- Qunit 和 jsCoverage使用方法
Qunit 和 jsCoverage使用方法(js单元测试) 近日在网上浏览过很多有关js单元测试相关的文档,工具,但是,针对Qunit 和 jsCoverage使用方法,缺少详细说明,对于初入前端的 ...