简单理解复合优先于继承

在看effective java时,有一条[复合优先于继承]的代码一直理解不了,不知道复合是什么意思,复合和继承到底有什么区别.网上代码都是 HashSet 的那个例子,不是很好理解 所以重新写了一段代码,方便理解.

首先看一个简单的例子
父类 Animal 代码

package com.example.springboot01.instance;

public class Animal {
    protected void print() {
        print2();
    }
    protected void print2() {
        System.out.println("Animal.print2()");
    }
}

子类 Dog 代码

package com.example.springboot01.instance;

public class Dog extends Animal {
    @Override
    public void print() {
        System.out.println("Dog.print()");
        super.print();
    }
    @Override
    public void print2() {
        System.out.println("Dog.print2()");
    }
}

测试类代码

    @Test
    public void testDog() {
        Dog dog = new Dog();
        dog.print();
    }

打印结果

Dog.print()
Dog.print2()

结果分析

  • 打印结果的第一行,没有问题,关键是第二行,打印出来的不是 Animal.print2() 而是 Dog.print2().
  • 原因是整个过程中调用方法的对象主体其实是 Dog 对象
  • 不管是 测试类中 dog.print();方法的调用,或者是 Dog 类中 super.print(); 方法的调用,还是 Animal 类中的 print2(); 方法的调用,其调用主体都是测试类中的那个 new Dog() 对象.
  • 所以最终Animal类中的那个 print2(); 方法调用等价于 new Dog().print2(); 方法调用.
  • 那么这样就容易产生一个问题,假如 Animal 类是一个公共类,其他类继承了它,并重写了里面的 print(); 和 print2(); 方法,那么很有可能会出现上面的那种情况,用户只调用了父类 Animal 的 print(); 方法,但是最终结果却还调用了子类 Dog 的 print2(); 方法,如果用户不去看父类 Animal 源码的话,是不知道自己重写的 print2(); 方法也被调用了,这就给程序开发埋下了隐患.
  • 那么这个问题怎么解决呢,其实也简单,就是变更方法调用的对象主体,之前出现问题是因为 对象主体一直是 new Dog(); 对象,那我们只要在调用父类 Animal 的方法时,将主体对象变更为 new Animal(); 对象就行了.

具体代码 Dog类 修改

package com.example.springboot01.instance;

public class Dog extends Animal {
    @Override
    public void print() {
        System.out.println("Dog.print()");
        // 修改前
        //super.print();
        // 修改后
        new Animal().print();
    }
    @Override
    public void print2() {
        System.out.println("Dog.print2()");
    }
}

修改后打印结果

Dog.print()
Animal.print2()
  • 这样就改好了,如果要进一步优化代码的话, new Animal(); 对象也可以通过构造器注入,修改后的代码如下

子类 Cat 通过构造器注入父类对象

package com.example.springboot01.instance;

public class Cat extends Animal {

    private final Animal animal;
    
    public Cat(Animal animal) {
        this.animal = animal;
    }
    @Override
    public void print() {
        System.out.println("Cat.print()");
        animal.print();
    }
    @Override
    public void print2() {
        System.out.println("Cat.print2()");
    }
}

修改后测试类

    @Test
    public void testDog() {
        Cat cat = new Cat(new Animal());
        cat.print();
    }

打印结果

Cat.print()
Animal.print2()
  • 通过构造器注入父类对象的代码是不是和effective java里面的示例代码 ForwardingSet类 有点类似?
  • 原理就是这样的,通过修改调用方法的主体对象,来达到避免误调方法的目的.
  • 现在再去看书中源码就好理解了,最好自己上手去执行一下代码,理解更容易.
  • 至于复合的含义,还是没懂.贴上原文的定义吧
  • 不扩展现有的类,而是在新的类中增加一个私有域,它引用现有类的一个实例 这种设计被称为“复合”(combination)

参考资料
Effective Java中文版(原书第3版)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值