• 欢迎访问天天编码网站,Java技术、技术书单、开发工具,欢迎加入天天编码
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏天天编码吧
  • 我们的淘宝店铺已经开张了哦,传送门:https://shop145764801.taobao.com/

深入详解Java中的继承与组合

Java基础 tiantian 2114次浏览 0个评论 扫描二维码

Java作为一门面向对象的语言,包含有两个功能相似的特性:继承与组合,他们都可以实现代码的复用。本文将带领大家一起来分析这两个的联系与区别,首先会用一个实例代码的形式来阐述继承机制,然后再展现如何使用组合机制来改进代码的设计。至于两者之间如何进行选择的问题,我们将在本文的总结部分给出。

深入详解Java中的继承与组合

继承

假设我们具有一个 Animal 类,该类包含有两个方法:move() 和 attack() 。

class Animal {
private int size;
private String color;

public Animal(int size, String color) {
this.size = size;
this.color = color;
}

public void move() {
System.out.println("www.tiantianbianma.com")
}

public void attact() {
move();
System.out.println("天天编码");
}

// getter methods and setter methods
}

如果你希望定义一个 Cat 类,该类也是一种 Animal ,但是该类的 attack() 和 move() 方法具有与 Animal 类不同的实现。这种情况下,我们使用继承机制来实现的话,代码将如下所示:

class Cat extends Animal {
public Cat(int size, String color) {
super(size, color);
}

public void move() {
System.out.println("QQ群:369189640");
}

public void attack() {
move();
super.attack();
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Cat(10, "red");
animal.attack();
}
}

建议大家先仔细思考得出答案,再上级验证,正确的输出是:

QQ群:369189640
QQ群:369189640
天天编码

从输出结果分析:Cat 类的 move() 方法被调用了两次。实际上,我们期待它只被调用一次。这个问题的发生是因为父类 Animal 中的 attack()方法,Cat 的 attack() 方法中有 super.attack()语句,此 super.attack() 又会调用 move() 方法,而这里调用的是 Cat 的 move() 方法。这个就是多态机制在发挥作用,如果还不是很明白的话,建议参看本站(天天编码)的 **Java中的重载(Overload)与重写(Override) 这篇文章。

针对此问题,我们可以执行的解决方法有二:
A) 删除掉子类 Cat 中的 attack() 方法。但是,这个方法会让子类完全依赖于父类中 attact() 方法的实现细节。如果,未来的某个时间点,父类中的 attack() 方法的实现细节被修改了(子类无法控制父类方法的实现细节),这将导致子类的 attack() 方法出现不可预料的行为。比如,如果父类使用了另一个方法去实现其功能,那么将迫使子类的该方法也必须具有同样的方法,这是一种很差的编程风格。
B) 把子类 Cat 中的 attack() 方法修改成如下所示:

public void attack() {
move();
System.out.println("天天编码");
}

这样修改以后,可以确保获得期望的输出结果,因为子类的方法不再依赖与任何父类方法的实现细节了。但是,这样的解决方法会导致子类与父类中有重复代码,这也是一种非常差劲的编程风格。当然了,实例代码中的重复代码比较少,只有一行,这种风格的缺点一样存在,完全不符合代码工程中的复用规则。

通过此实例,我们可以发现,基于继承机制来实现代码的复用功能是一种很不好的设计风格。因为,子类将会依赖与父类方法的具体实现细节,一旦父类发生改变,子类的功能可能就不正常了。

组合

&emps;除了继承机制之外,实现代码复用的另一个有效机制是组合机制。接下来,我们就来看看如何利用组合机制来实现同样的功能。首先把 attack 功能抽象为一个如下的接口(Interface):

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 功能抽象成为了一个接口,Animal 类就简化成为如下的形式了:

class Animal {
private int size;
private String color;

public Animal(int size, String color) {
this.size = size;
this.color = color;
}

// getter methods and setter methods
}

相应地,Cat 类将简化成如下的形式:

class Cat extentds Animal implements Attack {
private Attack attack;

public Cat(int size, String color, Attack attack) {
super(size, color);
this.attack = attack;
}

public void move() {
attack.move();
}

public void attack() {
attack.attack();
}
}

如果执行以下语句:

public class Main {
public static void main(String[] args) {
Cat catA = new Cat(10, "black", new AttackImpl("天天编码", "升职加薪"));
catA.attack();

// 如果想要另外一个 attack,非常简单。
Cat catB = new Cat(10, "black", new AttackImpl("天天编码", "当上CEO"));
catB.attack();
}
}

那么运行的结果是:

天天编码
升职加薪
天天编码
当上CEO

总结

正如本文前面所述,对于代码复用而言,Java中提供了继承、组合等多种语言特性,同时,他们又有着各自的优缺点。在实际的编码工作中,我们该如何选择呢?为此,本文总结了两个选择继承还是组合的依据:
1. 如果两个类之间本来就存在着 is-a 的关系,而且子类希望实现所有的父类接口,此时,继承机制就是合适。
2. 如果两个类之间是 has-a 的关系,此时,就应该使用组合机制。

所以说,继承机制和组合机制各有各的优点和适用场景。为了更好地使用Java的面向对象机制,读者应该能清楚地知道它们之间的联系与区别。


天天编码 , 版权所有丨本文标题:深入详解Java中的继承与组合
转载请保留页面地址:http://www.tiantianbianma.com/java-inherit-combine.html/
喜欢 (0)
支付宝[多谢打赏]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址