首页 > 编程语言 >Dating Java8系列之default默认方法

Dating Java8系列之default默认方法

时间:2024-01-12 23:45:09浏览次数:27  
标签:default void 接口 public 实现 默认 Dating 方法 Java8

给我馍馍/文

 

图片

 

图片

引言

 

传统上,Java程序的接口是将相关方法按照约定组合到一起。实现接口的类必须为接口中定义的每个方法提供一个实现,或者从父类中继承它的实现。

 

不断迭代的API

默认方法的引入就是为了,以兼容的方式,解决像 Java API这样的类库,演进迭代问题。

理解演进迭代

为了理解为什么一旦API发布之后,它的演进就变得非常困难,我们假设你是一个Github上的开源作者,兴致勃勃的写了一个开源项目,然后放到了Github上面。

没过多久你的项目就被其他用户Fork到本地,然后开始使用了起来,并且在项目中对你发布的一些接口进行了实现。

发布API几个月之后,你突然意识到接口中遗漏了一些功能。需要调整原来的接口,在其中新增方法,这样的话接口的易用性会更好。

不过,事情并非如此简单!你要考虑已经使用了你接口的用户,他们可能已经按照自身的需求实现了你的接口,倘若你更新了接口的API并重新进行了发布,那么所以实现了你的接口的地方,都需要进行改动。

图片

简而言之,向接口添加方法是诸多问题的罪恶之源;一旦接口发生变化,实现这些接口的类往往也需要更新,提供新添方法的实现才能适配接口的变化。如果你对接口以及它所有相关的实现有完全的控制,这可能不是个大问题。但是这种情况是极少的。

这就是引入默认方法的目的:它让类可以自动地继承接口的一个默认实现。

 

图片

概述

 

1.默认方法

默认方法是Java 8中引入的一个新特性,希望能借此以兼容的方式改进API。现在,接口包含的方法签名在它的实现类中也可以不提供实现。那么,谁来具体实现这些方法呢?实际上,缺失的方法实现会作为接口的一部分由实现类继承(所以命名为默认实现),而无需由实现类提供。

默认方法由default修饰符修饰,并像类中声明的其他方法一样包含方法体。比如,你可以像下面这样在集合库中定义一个名为Sized的接口,在其中定义一个抽象方法size,以及一个默认方法isEmpty:

public interface Sized {    int size();    default boolean isEmpty() {        return size() == 0;} }

这样任何一个实现了Sized接口的类都会自动继承isEmpty的实现。

2.使用默认方法

可选方法

你肯定碰到过这种情况,一个类实现了接口,不过却将一些实现方法进行留白,没有实现。

我们以Iterator接口为例来说。Iterator接口定义了hasNext、next,还定义了remove方法。Java 8 之前,由于用户通常不会使用该方法,remove方法常被忽略。因此,实现Interator接口的类通常会为remove方法放置一个空的实现,这些都是没有意义毫无用处的代码。采用默认方法之后,你可以为这种类型的方法提供一个默认的实现,这样实体类就无需在自己的实现中显式地提供一个空方法。比如,在Java 8中,Iterator接口就为remove方法提供了一个默认实现。

interface Iterator<T> { boolean hasNext();T next();default void remove() {            throw new UnsupportedOperationException();        }}

通过这种方式,你可以减少无效的模板代码。实现Iterator接口的每一个类都不需要再声明一个空的remove方法了,因为它现在已经有一个默认的实现。

行为的多继承

Java的类只能继承单一的类,但是一个类可以实现多接口。

下面是Java API中对ArrayList类的定义:

public class ArrayList<E> extends AbstractList<E>        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {        }

这个例子中ArrayList继承了一个类,实现了四个接口。因此ArrayList实际是 五个类型的直接子类,分别是:AbstractList,List,RandomAccess,Cloneable,Serializable。所以,在某种程度上,我们就有了类型的多继承。

 

由于Java 8中接口方法可以包含实现,类可以从多个接口中继承它们的行为(即实现的代码)。 让我们从一个例子入手,看看如何充分利用这种能力来为我们服务。

public interface MoveService {
void run();
default void flash() { System.out.println("闪现!!!"); }}public interface SkillService {
void q();
void w();
void e();
default void r() { System.out.println("默认大招:伤害100点"); }}public class Shooter implements MoveService, SkillService { @Override public void run() { System.out.println("寒冰射手 走~~"); } @Override public void q() { System.out.println("寒冰 q"); } @Override public void w() { System.out.println("寒冰 w"); } @Override public void e() { System.out.println("寒冰 e"); } public void r(){ System.out.println("寒冰 伤害100点!!同时冰冻对方5s!!!"); } public static void main(String[] args) { new Shooter().r(); }}

 

图片

方法冲突

 

我们知道Java语言中一个类只能继承一个类,但是一个类可以实现多个接口。
随着默认方法在Java 8中引入,有可能出现一个类继承了多个方法而它们使用的却是同样的函数签名。这种情况下,在一个类中使用父类的默认方法,这样会有冲突吗,没有的话,那会选择哪一个呢?

1.解决冲突的三条规则

如果一个类使用相同的函数签名从多个地方(比如另一个类或接口)继承了方法,通过三条规则可以进行判断。

  • 类中的方法优先级最高。类或父类中声明的方法的优先级高于任何声明为默认方法的优先级。

  • 如果无法依据第一条进行判断,那么子接口的优先级更高:函数签名相同时,优先选择拥有最具体实现的默认方法的接口,即如果B继承了A,那么B就比A更加具体。

  • 最后,如果还是无法判断,继承了多个接口的类必须通过显式覆盖和调用期望的方法,显式地选择使用哪一个默认方法的实现。

2.冲突示例

类中的方法优先级最高

public interface PlayerService {    default void stop() {        System.out.println("播放器--停止!!!");    }}
public class SonyPlayerServiceImpl implements PlayerService { public void stop() { System.out.println("Sony播放器--停止!!!"); } public static void main(String[] args) { new SonyPlayerServiceImpl().stop(); }}

选择提供了最具体实现的默认方法的接口

public interface PlayerService {    default void showLyric() {        System.out.println("PlayerService : show lyric");    }}public interface RecordService extends PlayerService{    default void showLyric() {        System.out.println("RecordService : show lyric");    }}public class Question1 implements RecordService {    public static void main(String[] args) {        new Question1().showLyric();        System.out.println("应该选择的是提供了最具体实现的默认方法的接口。由于RecordService比PlayerService更具体,所以应该选择由于RecordService比PlayerService更具体的showLyric方法。");    }}

冲突和显示的消除歧义

public class Question2 implements PlayerService,RecordService {    @Override    public void showLyric() {        PlayerService.super.showLyric();        RecordService.super.showLyric();    }
public static void main(String[] args) { new Question2().showLyric(); }}

 

图片

小结

 

  • Java 8中的接口可以通过默认方法提供方法的代码实现。

  • 默认方法的开头以关键字default修饰,方法体与常规的类方法相同。

  • 默认方法的出现能帮助库的设计者以后向兼容的方式演进API。

  • 默认方法可以用于创建可选方法和行为的多继承。

  • 我们有办法解决由于一个类从多个接口中继承了拥有相同函数签名的方法而导致的冲突。

  • 类或者父类中声明的方法的优先级高于任何默认方法。如果前一条无法解决冲突,那就选择同函数签名的方法中实现得最具体的那个接口的方法。

  • 两个默认方法都同样具体时,你需要在类中覆盖该方法,显式地选择使用哪个接口中提供的默认方法。

 

作者:给我馍馍
博客:https://www.cnblogs.com/givemomo/

 

本篇文章如有帮助到您,请给「给我馍馍」点个赞,感谢您的支持。

标签:default,void,接口,public,实现,默认,Dating,方法,Java8
From: https://www.cnblogs.com/givemomo/p/17961800

相关文章

  • Dating Java8系列之Java8中的流操作
    给我馍馍/文  本次我们会使用到很多的流操作,如筛选、切片、映射、查找、匹配和归约,这些操作可以让我们能快速完成复杂的数据查询。 筛选和切片 用谓词筛选Streams接口支持filter方法。该操作会接受一个谓词(一个返回boolean的函数)作为参数,并返回一个包括所有符合......
  • Dating Java8系列之Java8中的‘流’
    给我馍馍/文 流的概念 1.流是什么流是JavaAPI的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,我们可以把它们看成遍历数据集的高级迭代器。 代码:按价格排序后得到手机名称列表 使用新的流式方法有几个显而易见......
  • Dating Java8系列之巧用Optional之优雅规避NPE问题
    给我馍馍/文  避之不及的NullPointerException NPE:NullPointerException空指针异常是最常见的Java异常之一,抛出NPE错误不是用户操作的错误,而是开发人员的错误,应该被避免,那么只能在每个方法中加入非空检查,阅读性和维护性都比较差。以下是一个常见的嵌套对象:一个用......
  • Dating Java8系列之并行数据处理
    翎野君/文  分支合并框架 分支合并框架介绍分支/合并框架的目的是以递归的方式将可以并行的任务拆分成更小的任务,然后将每个子任务的结果合并起来生成整体结果。它是ExecutorService接口的一个实现,它把子任务分配给线程池(称为ForkJoinPool)中的工作线程。把任务提交......
  • java8日期时间格式化DateTimeFormatter多个格式
    原文地址:datetimeformatter.ofpatternmultipleformats-掘金DateTimeFormatter 是一个用于日期时间格式化和解析的类。使用 ofPattern 方法可以创建一个格式化器,该方法接受一个日期时间格式的字符串作为参数。如果您需要在同一个 DateTimeFormatter 对象中支持多种不同的......
  • knex迁移 - 'ER_INVALID_DEFAULT: 'timestamp'的默认值无效
    在Knex中使用.timestamp()方法时,默认情况下会为该列设置当前时间戳作为默认值。然而,根据你的错误提示,数据库可能不支持此默认值。为了解决这个问题,你可以尝试以下方法:如果你的数据库支持DEFAULTCURRENT_TIMESTAMP,你可以尝试在列定义中添加defaultTo(knex.fn.now(......
  • java8中object转list
    Java8中Object转List的实现概述在Java8中,我们可以使用StreamAPI将一个Object对象转换为List集合。本文将介绍如何使用StreamAPI实现此功能,并提供相应的示例代码。实现步骤下面是实现"Java8中Object转List"的步骤,我们可以使用以下表格形式展示:步骤描述1创建一个Obj......
  • java8找集合中最小的
    Java8找集合中最小的简介在Java编程中,我们经常需要在一个集合中寻找最小的元素。在Java8中,我们可以使用StreamAPI来实现这个功能。StreamAPI是Java8中引入的一个强大的功能,它可以让我们以一种更简洁、更易读的方式处理集合数据。本文将介绍如何使用Java8的StreamAPI来找到一个......
  • Java8 原子类 AtomicInteger 源码阅读
    AtomicInteger 是用 CAS(Compre And Swap,乐观锁)构造的一个 原子类。1. CAS CAS(CompareandSwap)比较并替换,CAS是实现乐观锁的一个重要操作。CAS是一个硬件指令,保证是原子操作,Java中通过UnSafe来实现。详细可一下我的这篇博文:传送。CAS 的基本步骤:执行函数CAS(V,E,N......
  • java8 新特性(二)CompletableFuture类
    CompletableFuture是Java8中引入的一个新特性,它表示异步计算的结果。通过使用CompletableFuture,可以方便地处理异步计算,并能够在计算完成后执行回调函数。CompletableFuture是Java8中引入的一个功能强大的类,它实现了Future接口,并在此基础上进行了丰富的扩展,以简化异步编程的复......