前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java基础系列(二十五):接口进阶

Java基础系列(二十五):接口进阶

作者头像
山禾说
发布2019-01-21 10:08:47
3640
发布2019-01-21 10:08:47
举报
文章被收录于专栏:Vi的技术博客Vi的技术博客

接口与抽象类

在前面章节的学习中,我们对于接口和抽象类都已经有了一个大概的理解和消化,现在我们将从语法层面设计层面两个方向来分析它们到底有什么不同:

(一)语法层面
  1. 抽象类可以有默认的方法实现,接口在Java版本1.8之前是完全抽象的,它根本不存在方法的实现,但是在1.8之后接口中可以存在静态方法和默认方法,本节下部分内容即将讲解默认方法在接口中的应用以及默认方法发生冲突的时候的两种情况。
  2. 抽象类子类可以使用extends关键字来继承抽象类,如果子类不是抽象类,它必须提供抽象类中所有声明的非默认方法的实现。而接口实现类需要使用关键字implements来是瞎按接口,它需要提供接口中所有声明的非默认方法的实现。
  3. 抽象类可以有构造器,接口不能有构造器。
  4. 与正常Java类的区别来说的话,抽象类除了不能实例化以外,与其他的普通Java类没有任何区别,但是接口却属于完全不同的另外一种类型。
  5. 抽象方法可以有public,protected和default来这些修饰符,接口的方法默认为public,只可以使用default来标记默认方法。
  6. 抽象类可以有main方法并且我们可以运行它,接口没有main方法,因此我们不可以运行它。
  7. 抽象类可以继承一个类和实现多个接口,接口只可以继承一个或多个其他接口。
  8. 从速度上来说,抽象类的速度是要优于接口的,因为接口需要时间去寻找在类中实现的方法。
(二)设计层面---摘自海子大神的博客
  1. 抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。从这里可以看出,继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。
  2. 设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,大家都用过ppt里面的模板,如果用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它们的公共部分需要改动,则只需要改动模板A就可以了,不需要重新对ppt B和ppt C进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。

默认方法

可以为接口提供一个默认实现,但是必须用default修饰符来标记这样一个方法:

代码语言:javascript
复制
public interface Comparable<T> {
    default int compareTo(T other) {return 0;}
}

默认方法的作用主要体现在两点:

  1. 我们有时可能需要接口提供很多方法,但是其中的大部分的方法都不是我们常用的方法,我们经常会用到的仅仅是其中的一小部分,这是就可以把接口中的方法都加上default关键词,而我们实现这个接口的时候主要去重写我们需要的方法即可,那些不经常的使用的方法我们不需要去care它。
  2. 默认方法的另外一个重要的作用是“接口演化”,比如说很久以前你提供了这样一个类:
代码语言:javascript
复制
public class Bag implements Collection

后来,在Java SE 8中,又为这个方法增加了一个stream方法。 假设 stream方法不是一个默认方法,那么Bag累将不能编译,因为它没有实现这个新方法,为接口增加一个非默认方法不能保证“源代码兼容”。 不过,假如不重新编译这个类,而只是使用原先的一个包含这个类的JAR文件。这个类仍然可以正常加载。程序仍然可以正常构造Bag实例,不会有意外发生。(为接口增加方法可以保证“二进制兼容”)。不过,如果程序在一个Bag实例上调用stream方法就会抛出一个AbstarctMethodError。 将方法实现为一个默认方法就可以解决这两个问题。Bag类又能正常编译了。另外如果没有重新编译而直接加载这个类,并在一个Bag实例上调用stream方法,将调用Collection.stream方法。

解决默认方法冲突

如果先在一个接口中将一个方法定义为默认方法,然后又在超类或另一个接口中定义了同样的方法,就会发生方法冲突,对于这种情况,Java给出的解决方案遵循以下两种规则:

  1. 超类优先。如果超类提供了一个具体方法,同名而且有相同参数类型的默认方法会被忽略。
  2. 接口冲突。如果一个超接口提供了一个默认方法,另一个接口提供了一个同名且参数类型(不论是否是默认参数)相同的方法,必须覆盖这个方法解决冲突。

这里需要注意一点,如果两个接口的方法发生了冲突,且只有一个方法提供了默认实现。从感性的角度,我们可能会觉得子类会去使用那个默认的实现,但是其实并不是如此,Java设计上强调了一致性。两个接口如何冲突不重要,如果至少有一个接口提供了一个实现,编译器就会报告错误,我们就必须去解决这个二义性。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-08-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Vi的技术博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 接口与抽象类
    • (一)语法层面
      • (二)设计层面---摘自海子大神的博客
      • 默认方法
      • 解决默认方法冲突
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档