今天看OOD启思录的时候,看到继承那章,回想到之前看Effective JAVA的时候,看到那里举的一个继承的危险性的例子,不过记得不是很精确,于是就翻出那本书:

第十四条,复合优先于继承。

那里说,与调用类的方法不同,继承打破了封装性[Snyder86]。换句话说,一个子类依赖于其超类中特定功能的实现细节。而超类的实现有可能会随着发行版本的不同而有所变化,如果真的发生了变化则子类可能会被打破,即使它的代码完全没有改变。一次一个子类必须要跟着其超类的更新而发展,除非超类是专门为了扩展而被设计的,并且具有很好的文档说明。

举个这样的例子:

public class InstrumentedHashSet extends HashSet {

private int addCount = 0;

........// constructors.

public boolean add( Object o ) {

addCount++;

return super.add( o );

}

public boolean addAll( Collection c ) {

addCount += c.size();

return super.addAll( c );

}

public int getAddCount() {

return addCount;

}

}

这个类看起来非常合理,但是它并不能正常工作。假如我们创建了一个实例,并使用addAll方法添加了三个元素:

InstrumentedHashSet s = new InstrumentedHashSet();

s.addAll( Arrays.asList( new String[] {"Snap","Crackle","Pop"} ) );

这时候,我们一般会期望getAddCount方法会返回3,但是它实际上返回6。错就出在子类没有考虑到HashSet内部的实现细节。HashSet的addAll方法是基于它的add方法来实现的,InstrumentedHashSet中的addAll方法首先给addCount增加3,然后通过super.allAll来调用HashSet的addAll实现。然后又顺次调用到被InstrumentedHashSet改写了的add方法,每个元素调用一次。这三次调用又分别给addCount多加了1,所以,总共增加了6:通过addAll方法增加的每个元素都被计算了两次。

在清楚超类的实现细节的话,可以通过修正addAll方法来使子类正确运行。可是,它的功能正确性需要依赖于超类的实现细节。这是一个“Self-use”的例子。而超类的这些实现细节并不是承诺,不能保证不会随着版本不同而不发生变化。所以这样得来的子类将是非常脆弱的。

所以,虽然继承机制的功能非常强大,但是它存在诸多问题。它违背了封装原则。

因此,Effective JAVA给出第十五条:要么专门为继承而继承,并给出文档说明,要么禁止继承。

很多时候可以用复合的方法避免继承。不再是扩展一个已有的类,而是在新的类中增加一个私有域,它引用了这个已有类的一个实例。新类的每一个实例方法都可以调用被包含的已有类的实例中对应的方法,并返回它的结果。这被称为转发(forwarding)。

public class InstrumentedSet implements Set {

private final Set s;

private int addCount = 0;

public InstrumentedSet( Set s ){

this.s = s;

}

........// Forwarding methods

}

通过复合和装发,每一个InstrumentedSet实例都把另一个set实例包装起来,所以这样的类可以被称作包装类(Wrapper class)。这也正是Decorator模式,因为InstrumentedSet类对一个集合进行了修饰,为它增加了计数特性。

java 打破封装_继承打破了封装性相关推荐

  1. W6_面向对象_封装_继承_多继承_多态

    W6_面向对象_封装_继承_多继承_多态 80.81.82.83.第02章节-Python3.5-面向对象介绍 84.第05章节-Python3.5-实例变量与类变量 85.第06章节-Python3 ...

  2. 在java面向对象编程中_谈一谈你对封装_继承_多态概念的理解_Java新职篇:面向对象编程的3个原则是什么?...

    原标题:Java新职篇:面向对象编程的3个原则是什么? 所有面向对象的编程语言都提供帮助你实现面向对象模型的机制,这些机制是封装,继承及多态性.现在让我们来看一下它们的概念. 封装 封装(Encaps ...

  3. CPP_封装_继承_多态

    类的三方法:封装,继承,多态. 封装:使用一整套方法去创建一个新的类型,这叫类的封装. 继承:从一个现有的类型基础上,稍作改动,得到一个新的类型的方法,叫类的继承. 多态:当有几个不同的子类对象时,对 ...

  4. httpurlconnection 封装_不要再封装各种Util工具类了,看看这个框架

    不要再封装各种Util工具类了,看看这个框架 Hutool 谐音 "糊涂",寓意追求 "万事都作糊涂观,无所谓失,无所谓得" 的境界.    Hutool 是一 ...

  5. Java学习笔记_继承

    继承的格式 在继承关系中,"子类就是一个父类".也就是说,子类可以被当作父类看待 例如父类是员工,子类是讲师,那么"讲师就是一个员工" 定义父类的格式:(一个普 ...

  6. ad中电容用什么封装_【AD封装】VH3.96mm插件座子(带3D)

    VH3.96mm插件座子 包含了我们平时常用的VH3.96mm间距的插件座子,总共28种封装及精美3D模型.完全能满足日常设计使用.每个封装都搭配了精美的3D模型哦. ❖ VH一般是3.96mm线到板 ...

  7. python小工具封装_使用 Docker 封装 Python 小工具生成 GitBook PDF

    众所周知 GitBook 新版本生成的 PDF 是调用 calibre 的 ebook-convert 模块进行电子书生成的,而它默认生成的 PDF 尺寸比较大,而且不支持压缩,非常不利于传播. 经过 ...

  8. 如何根据原理图画封装_常用原理图封装

    原理图常用库文件: Miscellaneous Devices.ddb Dallas Microprocessor.ddb Intel Databooks.ddb Protel DOS Schemat ...

  9. golang mysql封装_使用Golang 封装一个Api 框架 ----- 数据库操作篇(gorm引入)

    在models/baseModel 定义了 一个BaseModel对象,在这个对象上定义了 获取数据库连接和 释放数据库连接的方法 type BaseModel struct { dbConnect ...

最新文章

  1. 取代MybatisPlus?阿里推出了新 ORM 框架!(两者对比参考)
  2. 将webstorm设置为eclipse风格
  3. C++ string源码
  4. oracle mysql 并发连接数_如何修改Oracle并发连接数的设置
  5. CentOS 7安装 ifconfig 管理命令
  6. SQL语句中的select高级用法
  7. php简单文件上传类
  8. mvnrepository总是出现烦人的one more step验证
  9. abp vnext2.0核心组件之DDD组件之实体结构源码解析
  10. 逻辑性不好可以学python吗_如果本文若未能让你学会“Python”,可能真的不适合学习Python...
  11. Windows三十年进化史,从Windows 1.0到Windows 10
  12. 小米超大杯旗舰不叫12 Ultra:或命名为MIX 5 Pro
  13. sql主键可以有多个_干货 | 新手请速戳!30个精选SQL面试问题Qamp;A集锦
  14. 关于利用DEM生成水系图
  15. JMDM 楼宇电梯智能实时监控系统解决方案
  16. webstorm 2018 激活破解方法大全
  17. kaldi的安装使用
  18. HTML5 validity api,html5 form-Validity验证函数
  19. 使用Tensorflow实现声纹识别
  20. 操作系统二轮复习(进程的同步与互斥)

热门文章

  1. windows下安装ubuntu16.04双系统的注意事项
  2. opencv中的Mat图使用CDC显示
  3. PAT 1059 Prime Factors[难]
  4. (一)Maven 基本概念——根目录、项目创建、坐标
  5. Spring MVC 学习总结(五)——校验与文件上传
  6. CAnimation-模拟时钟
  7. onclick 事件
  8. 快速理解聚合根、实体、值对象的区别和联系
  9. spring boot 使用application.properties 进行外部配置
  10. 更改自身web项目的图标(默认为tomcat的小喵咪)