为什么有的地方必须使用接口而不是抽象类,而在另一些地方,又必须使用抽象类而不是接口呢?或者说,在考虑Java类的一般化问题时,很多人会在接口和抽象类之间犹豫不决,甚至随便选择一种。
  

首先来了解一下接口和抽象类的一些具体的区别:

  抽象类表示该类中可能已经有一些方法的具体定义,但是接口就仅仅只能定义各个方法的界面(方法名,参数列表,返回类型),并不关心具体细节。

类描述了一个实体,包括实体的状态,也包括实体可能发出的动作。

  接口定义了一个实体可能发出的动作。但是只是定义了这些动作的原型,没有实现,也没有任何状态信息。

  所以接口有点象一个规范、一个协议,是一个抽象的概念;而类则是实现了这个协议,满足了这个规范的具体实体,是一个具体的概念。

  从程序角度,简单理解,接口就是函数声明,类就是函数实现。需要注意的是同一个声明可能有很多种实现。

  1、接口中定义类方法的原型,但是不能说是空方法,因为空方法的意思是有实现体,只不过实现体是空操作。实际上接口没有定义任何实现体。具体的实现体都是在实现接口的类中,接口只是定义了这些方法的调用方式。

  你当然也可以不用接口,直接在类里面写方法,但是如果你的一组方法需要在很多类里实现,那么把它们抽象出来,做成一个接口规范,不是更好么?

  2、一个类描述了一个实体,这个实体可能是一个复杂的对象,它的动作很多,如果把这些动作分类,用接口a定义其中的某一组动作,接口b定义其中的另外一组动作,这样的结构,比较清楚。

  这种方式具备了多继承的优点,避免了多继承的缺陷。实际上在历史上,接口在很大程度上,是为了解决多继承带来的种种问题而设计出来的。

  3、包中那些已定义的接口,怎么知道那里面定义了什么方法。

  接口里定义了方法的输入输出,这些都是协议,具体的实现都在每个类中。对于很多只需要抽象接口的地方,不需要知道具体的类是什么,只要这个类实现了这个接口就可以了。

实际上接口和抽象类的选择不是随心所欲的。 要理解接口和抽象类的选择原则,有两个概念很重要:对象的行为和对象的实现。如果一个实体可以有多种实现方式,则在设计实体行为的描述方式时,应当达到这 样一个目标:在使用实体的时候,无需详细了解实体行为的实现方式。也就是说,要把对象的行为和对象的实现分离开来。既然Java的接口和抽象类都可以定义 不提供具体实现的方法,在分离对象的行为和对象的实现时,到底应该使用接口还是使用抽象类呢? 
  
  通过抽象类建立行为模型
  
    在接口和抽象类的选择上,必须遵守这样一个原则:行为模型应该总是通过接口而不是抽象类定义。为了说明其原因,下面试着通过抽象类建立行为模型,看看会出现什么问题。 
  
    假设要为销售部门设计一个软件,这个软件包含一个“发动机”(Motor)实体。显然无法在发动机对象中详细地描述发动机的方方面面,只能描述某些对当前软件来说重要的特征。至于发动机的哪些特征是重要的,则要与用户(销售部门)交流才能确定。 
  
    销售部门的人要求每一个发动机都有一个称为马力的参数。对于他们来说,这是惟一值得关心的参数。基于这一判断,可以把发动机的行为定义为以下行为。
  
    行为1:查询发动机的马力,发动机将返回一个表示马力的整数。 
  
    虽然现在还不清楚发动机如何取得马力这个参数,但可以肯定发动机一定支持这个行为,而且这是所有发动机惟一值得关注的行为特征。这个行为特征既可 以用接口定义,也可以用抽象类定义。为了说明用抽象类定义可能出现的问题,下面用抽象类建立发动机的行为模型,并用Java方法描述行为1,代码如下: 
  
  
  
  public abstract Motor{
  
  abstract public int getHorsepower(); 
  
  }
  
  
  
    在Motor抽象类的基础上构造出多种具体实现,例如A型发动机、B型发动机等,再加上系统的其它部分,最后得到1.0版的软件并交付使用。一段 时间过去了,现在要设计2.0版的软件。在评估2.0版软件需求的过程中,发现一小部分发动机是电池驱动的,而电池需要一定的充电时间。销售部门的人希望 能够通过计算机查阅充电时间。根据这一要求定义一个新的行为,如图1所示。 
  
    行为2:查询电驱动发动机的充电时间,发动机将返回一个表示充电时间的整数。 
  
    用Java方法来描述这个行为,代码如下: 
  
  
  
  public abstract BatteryPoweredMotor extends Motor{
  
  abstract public int getTimeToRecharge();
  
  }
  
  
  
    在销售部门的软件中,电驱动发动机也以类的形式实现,但这些类从BatteryPoweredMotor而不是Motor派生。这些改动加入到 2.0版软件之后,销售部门很满意。随着业务的不断发展,不久之后光驱动的发动机出现了。销售部门要求光驱动发动机需要一定光能才能运转,光能以流明 (Lumen)度量。这个信息对客户很重要,因为下雨或多云的天气里,某些光驱动发动机可能无法运转。销售部门要求为软件增加对光驱动发动机的支持,所以 要定义一个新的行为。
  
    行为3:查询光驱动发动机能够正常运转所需要的最小流明数,发动机返回一个整数。 
  
    再定义一个抽象类并把行为3转换成Java方法,代码如下:

public abstract SolarPoweredMotor extends Motor{
  
  abstract public int getLumensToOperate();
  
  } 
  
    
   
    如图1所示,SolarPoweredMotor和BatteryPoweredMotor都从Motor抽象类派生。在整个软件中,90%以上 的代码以相同的方式对待所有的发动机。偶尔需要检查一下发动机是光驱动还是电驱动,使用instanceof实现,代码如下: 
  
  [color=#336600]
  
  if (instanceof SolarPoweredMotor){...} 
  
  if (instanceof BatteryPoweredMotor){...}
  
  
  
    无论是哪种发动机,马力这个参数都很重要,所以在所有派生的抽象类(SolarPoweredMotor和BatteryPoweredMotor)中,getHorsepower()方法都有效。
  
    现在销售部门又有了一种新的发动机,它是一种既有电驱动又有光驱动的双重驱动发动机。光驱动和电驱动的行为本身没有变化,但新的发动机同时支持两 种行为。在考虑如何定义新型的光电驱动发动机时,接口和抽象类的差别开始显示出来了。新的目标是在增加新型发动机的前提下尽量少改动代码。因为与光驱动发 动机、电驱动发动机有关的代码已经过全面的测试,不存在已知的Bug。为了增加光电驱动发动机,要定义一个新的SolarBatteryPowered抽 象类。如果让SolarBatteryPowered从Motor抽象类派生,SolarBatteryPowered将不支持针对光驱动发动机和电驱动 发动机的instanceof操作。也就是说,如果查询一个光电驱动的发动机是光驱动的,还是电驱动的,得到的答案是:都不是。 
  
    如果让SolarBatteryPowered从SolarPoweredMotor(或BatteryPoweredMotor)抽象类派生, 类似的问题也会出现,SolarBatteryPowered将不支持针对BatteryPoweredMotor(或 SolarPoweredMotor)的instanceof操作。从行为上看,光电驱动的发动机必须同时从两个抽象类派生,但Java语言不允许多重继 承。之所以会出现这个问题,根本的原因在于使用抽象类不仅意味着定义特定的行为,而且意味着定义实现的模式。也就是说,应该定义一个发动机如何获得行为的 模型,而不仅仅是声明发动机具有某一个行为。 
  
  通过接口建立行为模型
  
    如果用接口来建立行为模型,就可以避免隐含地规定实现模式。例如,前面的几个行为改用接口定义如下。 
  
    行为1:
  
  
  
  public interface Motor(){
  
  public int getHorsepower();
  
  }
  
  
  
  行为2:
  
  
  
  public interface BatteryPoweredMotor extends Motor(){
  
  public int getTimeToRecharge();
  
  }
  
  
  
  行为3:
  
  
  
  public interface SolarPoweredMotor extends Motor{
  
  abstract public int getLumensToOperate();
  
  }
  
  
  
    现在光电驱动的发动机可以描述为: 
  
  
  public DualPoweredMotor implements SolarPoweredMotor, BatteryPoweredMotor{}
  
   
    DualPoweredMotor只继承行为定义,而不是行为的实现模式, 
  
    在使用接口的同时仍旧可以使用抽象类,不过这时抽象类的作用是实现行为,而不是定义行为。只要实现行为的类遵从接口定义,即使它改变了父抽象类, 也不用改变其它代码与之交互的方式。特别是对于公用的实现代码,抽象类有它的优点。抽象类能够保证实现的层次关系,避免代码重复。然而,即使在使用抽象类 的场合,也不要忽视通过接口定义行为模型的原则。从实践的角度来看,如果依赖于抽象类来定义行为,往往导致过于复杂的继承关系,而通过接口定义行为能够更 有效地分离行为与实现,为代码的维护和修改带来方便。

转载于:https://www.cnblogs.com/hoobey/p/6534173.html

接口与抽象类的使用选择相关推荐

  1. 选择Java接口还是抽象类

    很多人有过这样的疑问:为什么有的地方必须使用接口而不是抽象类,而在另一些地方,又必须使用抽象类而不是接口呢?或者说,在考虑Java类的一般化问题时,很多人会在接口和抽象类之间犹豫不决,甚至随便选择一种 ...

  2. 什么时候使用接口?什么时候使用抽象?选择Java接口还是抽象类 (转)

    老帖了,但是还是想自己收录一下.最后也把自己对于这个问题的一些粗糙的理解记录下来. 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的.在这种情况下,各个对象内部是如何实现自己的对 ...

  3. 有关接口和抽象类的问题

    1. 一个子类如果implements一个接口,就必须实现接口中的所有方法(不管是否需要):如果是继承一个抽象类,只需要实现需要的方法即可,这是抽象类的一个优点 2. 如果一个接口中定义的方法名改变了 ...

  4. 第18条:接口优于抽象类

    为什么80%的码农都做不了架构师?>>>    1.内容导向 本条内容的主要重点--骨架类,集成接口和抽象类的优点于一身. 2.接口的优点 现有的类可以很容易的被更新,以实现新的接口 ...

  5. Java中接口、抽象类与内部类学习

    2019独角兽企业重金招聘Python工程师标准>>> Java中接口.抽象类与内部类学习 接口与内部类为我们提供了一种将接口与实现分离的更加结构化的方法. 抽象类和抽象方法 抽象方 ...

  6. C#读书笔记之继承、接口与抽象类续

    C#读书笔记之继承.接口与抽象类 http://www.cnblogs.com/linjzong/articles/2717173.html 接下来说明最开始提出的问题:为什么要使用接口和抽象类? 刚 ...

  7. 接口和抽象类的区别。

    接口是公开的,里面不能有私有的方法或变量,是用于让别人使用的,而抽象类是可以有私有方法或私有变量的, 接口里都是抽象方法,抽象类里可以有普通方法. 另外,实现接口的一定要实现接口里定义的所有方法,而实 ...

  8. 这一次,让你彻底明白接口及抽象类

    本文的目的是讨论抽象类和接口的作用.示例和使用场景,这是我的理解和总结.对于接口和抽象类的更多概念性知识,你可以自己参考相关文档. 1. 抽象类及其作用 抽象类,顾名思义,即类的抽象. 当引入面向对象 ...

  9. 我应该如何解释接口和抽象类之间的区别?

    本文翻译自:How should I have explained the difference between an Interface and an Abstract class? In one ...

最新文章

  1. 前端开发进阶手册.pdf
  2. ASP——Request.Form()方法获取POST提交方式的表单中文乱码解决方案
  3. 7-6 jmu-Java-02基本语法-06-枚举 (3 分)
  4. logisim输出变成红色的e_新车实拍解析 福特Mustang Mach-E亮点实拍图解
  5. Js 枚举定义Layer Icon
  6. jquery 统计统计子标签的个数
  7. 25. 合并两个排序的链表
  8. html浮动标签调整位置,HTML 获取当前标签在页面的位置,并且把其他的浮动窗口显示过来...
  9. 03-24 CPU 统计
  10. 大数据分析平台如何选择
  11. 转:Ajax调用Webservice和后台方法
  12. 修改Hosts不生效的解决办法
  13. Unity 脚本入门教程
  14. 批量cherry-pick
  15. echart x轴 倾斜,Echarts x轴字体倾斜
  16. 如何基于 RISC-V CPU 集成一个 RISC-V SoC 呢?(上)
  17. 淘宝天猫店招空白间隔去除
  18. STM32芯片烧录的三种方式介绍,串口、STM32 ST-LINK Utility以及STM32CubeProgrammer
  19. SQL中连接JOINS总结
  20. cufflinks之cuffmerge,cuffdiff

热门文章

  1. Go 学习笔记(79)— Go 标准库 net(获取本机IP地址)
  2. Go 学习笔记(71)— Go 接口 interface (接口定义、接口实现、接口调用、值接收者、指针接收者)
  3. 两个月番茄组长的收获总结
  4. 确定修改——取消修改
  5. 使用Auto TensorCore CodeGen优化Matmul
  6. NVIDIA GPU卷积网络的自动调谐
  7. 使用现代C++如何避免bugs(下)
  8. 怎样训练YOLOv3
  9. 2021年大数据常用语言Scala(四):基础语法学习 声明变量
  10. [JAVA EE] 内联用法