Java中的继承与组合

本文主要说明Java中继承与组合的概念,以及它们之间的联系与区别。首先文章会给出一小段代码示例,用于展示到底什么是继承。然后演示如何通过“组合”来改进这种继承的设计机制。最后总结这两者的应用场景,即到底应该选择继承还是组合。

1、继承

假设我们有一个名为Insect(昆虫)的类,这个类包含两个方法:1)移动move(); 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();  //假设昆虫在攻击前必须要先移动一次

System.out.println("Attack");

}

}

现在,你想要定义一个名为Bee(蜜蜂)的类。Bee(蜜蜂)是Insect(昆虫)的一种,但实现了不同于Insect(昆虫)的attack()和move方法。这时候我们可以用继承的设计机制来实现Bee类,就像下面的代码一样:

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();

}

}

InheritanceVSComposition作为一个测试类,在其main方法中生成了一个Bee类的实例,并赋值给Insect类型的引用变量 i。所以调用i的attack方法时,对应的是Bee类实例的attack方法,也就是调用了Bee类的attack方法。

类的继承结构图如下,非常简单:

输出:

输出:

Fly

Fly

Attack

Fly被打印了两次,也就是说move方法被调用了两次。但按理来讲,move方法只应当被调用一次,因为无论是昆虫还是蜜蜂,一次攻击前只移动一次。

问题出在子类(即Bee类)的attack方法的重载代码中,也就是super.attack()这一句。因为在父类(即Insect类)中,调用 attack方法时会先调用move方法,所以当子类(Bee)调用super.attack()时,相当于也同时调用了被重载的move方法(注意是子 类被重载的move方法,而不是父类的move方法)。

为了解决这个问题,我们可以采取以下办法:

删除子类的attack方法。这么做会使得子类的attack方法的实现完全依赖于父类对于该方法的实现(因为子类继承了父类的attack方法)。如果 父类的attack方法不受控制而产生了变更。比如说,父类的attack方法中调用了另外的move方法,那么子类的attack方法也会产生相应的变 化,这是一种很糟糕的封装。

也可以重写子类的attack方法,像下面这样:

public void attack() {

move();

System.out.println("Attack");

}

这样保证了结果的正确性,因为子类的attack方法不再依赖于父类。但是,子类attack方法的代码与父类产生了重复(重复的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功能已经被抽象为一个接口(腾云科技ty300.com),所以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;

}

}

Bee类一种Insect类,它具有attack的功能,所以它实现了attack接口:

// 这个封装类封装了一个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类的构造函数:

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、什么时候该用继承,什么时候该用组合?

以下两条原则说明了应该如何选择继承与组合:

  • 如果存在一种IS-A的关系(比如Bee“是一个”Insect),并且一个类需要向另一个类暴露所有的方法接口,那么更应该用继承的机制。
  • 如果存在一种HAS-A的关系(比如Bee“有一个”attack功能基础教程qkxue.net),那么更应该运用组合。

总结来说,继承和组合都有他们的用处。只有充分理解各对象和功能之间的关系,才能充分发挥这两种机制各自的优点。

参考:       

    1. http://qkxue.net/info/22865/Java
    2. http://stackoverflow.com/questions/49002/prefer-composition-over-inheritance
    3. http://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition–which-one-should-you-choose-.html
时间: 2024-10-08 04:09:13

Java中的继承与组合的相关文章

菜鸟译文(一)——Java中的继承和组合

阅读英文的能力对于程序员来说,是很重要的.这几年也一直在学习英文,今天心血来潮,就在网上找了一篇简短的博文翻译一下.水平一般,能力有限,还请各位看官多多指点. 译文: 本文将会举例说明Java中继承和组合的概念.首先举一个继承的例子,然后展示一下如何用组合来改善继承的设计.最后概括一下如何在它们之间做出选择. 1. 继承 假设我们有一个Insect类.这个类包含两个方法:一个是move(),一个是attack(). class Insect { private int size; private

<Java中的继承和组合之间的联系和区别>

1 //Java中的继承和组合之间的联系和区别 2 //本例是继承 3 4 class Animal 5 { 6 private void beat() 7 { 8 System.out.println("心胀跳动..."); 9 } 10 public void breath() 11 { 12 beat(); 13 System.out.println("吸一口气,吐一口气,呼吸中..."); 14 } 15 } 16 //继承Animal,直接复用父类的bre

Java中的继承与组合(转载)

本文主要说明Java中继承与组合的概念,以及它们之间的联系与区别.首先文章会给出一小段代码示例,用于展示到底什么是继承.然后演示如何通过“组合”来改进这种继承的设计机制.最后总结这两者的应用场景,即到底应该选择继承还是组合. 1.继承 假设我们有一个名为Insect(昆虫)的类,这个类包含两个方法:1)移动move(): 2)攻击attack().代码如下: class Insect { private int size; private String color; public Insect(

JAVA中的继承

1.什么是继承 基于一个已存在的类,创建一个新的类.已存在的类即父类,新的类即子类,继承就是子类继承并拥有父类的属性和方法,同时,子类还有拥有父类所不具有的属性和方法. 父类,也称为基类.超类(superclass):子类,也称为派生类. 2.JAVA中"继承"的特点 JAVA中一个类只能继承一个父类.不像C++等语言那样,可以继承多个类.这也是JAVA比较容易学的一方面 只能继承父类中非private成员属性和方法,private是父类所特有的不能继承 3.JAVA中的"继

java中子类继承

[[email protected] java]# vim Ostrich.java //注意文件名必须是这个,因为下面代码中只有Ostrich是public修饰符.我们要明白public的含义 class Bird { public void Fly() { System.out.println("I am bird I can fly"); } } public class Ostrich extends Bird { public void Fly() { System.out.

Java中的继承、封装、多态的理解

Java中的继承.封装.多态 继承的理解: 1.继承是面向对象的三大特征之一,也是实现代码复用的重要手段.Java的继承具有单继承的特点,每个子类只有一个直接父类. 2.Java的继承通过extends关键字来实现,实现继承的类被称为子类,被继承的类称为父类(有的也称其为基类.超类),父类和子类的关系,是一种一般和特殊的关系.就像是水果和苹果的关系,苹果继承了水果,苹果是水果的子类,水果是苹果的父类,则苹果是一种特殊的水果. 3.Java使用extends作为继承的关键字,extends关键字在

java中的继承与oc中的继承的区别

为什么要使用继承? 继承的好处: (1)抽取出了重复的代码,使代码更加灵活 (2)建立了类和类之间的联系 继承的缺点: 耦合性太强 在OC中的继承是: 1.OC中不允许子类和父类拥有相同名称的成员变量名:(java中是可以的) 2.编译器从上往下执行,所以在子类前面至少应该要有父类的声明:如 @interface Worker : Person @end 3.OC中的子类可以拥有和父类相同名称的方法,在子类调用时,优先去自己的内部寻找,如果父类没有定义该方法,则继续在继承链上查找,直到找到为止,

Java中的继承、封装、多态

继承 所谓封装,就是将对象的成员变量和成员函数包装和隐藏起来,对外界通过特定的接口来访问. public class User { private String name; public User (String name) { this.name = name; } public String getName () { return this.name; } public void sayName () { System.out.println(this.getName()); } publi

java中的继承关系

1.定义 java中的继承是单一的,一个子类只能拥有一个父类:java中所有类的父类是java.lang.Object,除了这个类之外,每个类只能有一个父类: 而一个父类可以有多个子类,可以被多个子类继承: Java只支持单继承,也就是说,一个类不能继承多个类. Java只支持单继承(继承基本类和抽象类),但是我们可以用接口来实现(多继承{实现}接口来实现),脚本结构如: public class One extends Parent implements Two,Three,Four{} 2.