类的组合
2020年8月5日大约 3 分钟约 842 字
1. 概述
不同于继承,组合是将旧类对象作为新类的成员变量组合进来,用以实现新类的功能,用户看到的是新类的方法,而不能看到被组合对象的方法。因此,通常需要在新类里使用private
修饰被组合的旧类对象。
组合与继承都是类的重用,它们有一个共同的特点:在组合的整体类中,包含着部件类的子对象;在继承的派生类的对象中,包含着超类的子对象。
组合表达“包含”关系,继承表达“属于”关系。
继承的写法:
public class InheritTest { public static void main(String[] args) { Bird b = new Bird(); b.breath(); b.fly(); Wolf w = new Wolf(); w.breath(); w.run(); } } class Animal { private void beat() { System.out.println("心脏跳动..."); } public void breath() { beat(); System.out.println("吸一口气,吐一口气,呼吸中..."); } } // 继承Animal,直接复用父类的breath()方法 class Bird extends Animal { public void fly() { System.out.println("我在天空自在地飞翔..."); } } // 继承Animal,直接复用父类的breath()方法 class Wolf extends Animal { public void run() { System.out.println("我在陆地上的快速奔跑..."); } }
组合的写法:
public class CompositeTest { public static void main(String[] args) { Animal a1 = new Animal(), a2 = new Animal(); Bird b = new Bird(a1); b.breath(); b.fly(); Wolf w = new Wolf(a2); w.breath(); w.run(); } } class Animal { private void beat() { System.out.println("心脏跳动..."); } public void breath() { beat(); System.out.println("吸一口气,吐一口气,呼吸中..."); } } // 将原来的父类组合到原来的子类,作为子类的一个组合成分 class Bird { private Animal a; public Bird(Animal a) { this.a = a; } public void breath() { // 直接复用Animal提供的breath()方法来实现Bird的breath()方法 a.breath(); } public void fly() { System.out.println("我在天空自在地飞翔..."); } } // 将原来的父类组合到原来的子类,作为子类的一个组合成分 class Wolf { private Animal a; public Wolf(Animal a) { this.a = a; } public void breath() { // 直接复用Animal提供的breath()方法来实现Wolf的breath()方法 a.breath(); } public void run() { System.out.println("我在天空自在地飞翔..."); } }
上述使用组合关系来实现复用时,需要创建两个animal
对象,但要意识到这不意味着使用组合关系时系统的开销更大。因为之前已经介绍到,当创建一个子类对象时,系统不仅需要为该子类定义的实例变量分配内存空间,而且需要为它的父类所定义的实例变量分配内存空间。综合而言,继承设计与组合设计的系统开销不会有本质的区别。
2. 何时用继承,何时用组合?
- 继承是对已有的类做一番改造,以此获得一个特殊的版本,即,将一个较为抽象的类改造成能适用于某些特定需求的类。因此,上面对于
Wolf
和Animal
的关系,使用继承更能表达其现实意义; - 组合适用于两个类之间有明确的整体、部分的关系,如
Person
类需要复用Arm
类的方法,此时就应采用组合关系来实现复用,借助Arm
来实现Person
。 - 继承是一种 is-a 的关系,组合是一种 has-a 的关系。
- 有些问题既能用组合来表示,又能用属于来表示,这种情况下首选组合,因为组合的语法更简单一些。