美文网首页
Java8学习笔记之默认方法

Java8学习笔记之默认方法

作者: 夏与清风 | 来源:发表于2019-07-30 16:00 被阅读0次

    一、默认方法概述

    传统Java接口的弊端:Java程序接口是将相关方法安装约定组合到一起的方式,实现接口的类必须为接口中定义的每个方法提供一个实现,或者从父类中继承它的实现。而一旦类库需要更新接口,向其中增加新的方法,此方式就会出现问题。更为棘手的情况是,现存的实体类往往不在接口设计者的控制范围内,这些实体类为了适配新的接口约定也需要进行修改。Java8的API在现存的接口上引入了很多新方法,这种变化带来的问题更加严重。

    Java 8为了解决这一问题引入了一种新的机制,Java8中的接口支持在声明方法的同时提供实现,通过以下两种方式实现:

    1)Java8允许在接口内声明静态方法。

    2)Java8引入了一个新功能:默认方法,通过默认方法可以指定接口方法的默认实现,即接口可以提供方法的具体实现。实现接口的类如果不显示的提供该方法的具体实现,就会自动继承默认的实现。

    List接口中的sort新方法定义:

    default void sort(Comparator<? super E> c){

        Collections.sort(this, c);

    }

    默认方法的返回类型前用default修饰,表示这是一个默认方法。

    List<Integer> numbers = Arrays.asList(3, 5, 12, 32, 6);

    numbers.sort(Comparator.naturalOrder()); //sort是List接 口的默认方法

    Comparator.naturalOrder方法是一个全新的静态方法,它返回一个Comparator对象,并按照

    自然序列对其中的元素排序。

    default Stream<E> stream() {

        return StreamSupport.stream(spliterator(), false);

    }

    stream方法中调用了 SteamSupport.stream方法来返回一个流,其中spliterator()也是Collection接口的一个默认方法。

    Collection接口定义

    默认方法的主要目标用户是类库的设计者,它的引入是为了以兼容的方式解决像Java API这样的类库演进问题的。

    向接口添加方法

    所以,向接口添加方法是造成这些问题的根源;一旦接口发生变化,实现这些接口的类往往也需要更新,提供新添方法的实现才能适配接口的变化。引入默认方法的目的:它让类可以自动地继承接口的一个默认实现。

    默认方法为方法的多继承提供了一种更灵活的机制,可以帮助你更好地规划代码结构:类可以从多个接口继承默认方法。

    不同类型的兼容性:二进制、源代码和函数行为

    变更对Java程序的影响大体可以分成三种类型的兼容性,分别是:二进制级的兼容、源代码级的兼容,以及函数行为的兼容。

    \bullet 二进制级的兼容性表示现有的二进制执行文件能无缝持续链接(包括验证、准备和解析) 和运行。为接口添加方法就是二进制级的兼容,如果新添加的方法不被调用,接口已经实现的方法可以继续运行,不会出现错误。

    \bullet 源代码级的兼容性表示引入变化后,现有的程序依然能成功编译通过。向接口添加新的方法就不是源码级的兼容,因为遗留代码并没有实现新引入的方法,它们无法顺利通过编译。

    \bullet 函数行为的兼容性表示变更发生后,程序接受同样的输入能得到同样的结果。为接口添加新的方法就是函数行为兼容的,因为新添加的方法在程序中并未被调用。

    默认方法是Java 8中引入的一个新特性,借此以兼容的方式改进API。接口包含的方法签名在它的实现类中可以不提供实现,缺失的方法实现会作为接口的一部分由实现类继承(所以命名为默认实现),而无需由实现类提供。任何一个实现了接口的类都会自动继承其默认方法的实现。

    默认方法在Java 8的API中已经大量地使用了,如Collection接口的stream方法、List接口的sort方法、很多函数式接口(Predicate、Function、Comparator)也引入了新的默认方法,如Predicate.and或者Function.andThen。

    注:函数式接口只包含一个抽象方法,默认方法是种非抽象方法。

    Java 8中的抽象类和抽象接口区别:

    \bullet 一个类只能继承一个抽象类,但是一个类可以实现多个接口。

    \bullet 一个抽象类可以通过实例变量(字段)保存一个通用状态,而接口是不能有实例变量的。

    所有的Collection类都实现了一个名为java.util.Collection的接口。默认方法是一种以源码兼容方 式向接口内添加实现的方法,这样实现Collction的所有类(包括并不隶属Collection API的用户扩展类)都能使用removeIf的默认实现。

    removeIf的实现

    二、默认方法的使用模式

    1、可选方法

    我们可能会碰到过这种情况,类实现了接口,不过却刻意地将一些方法的实现留白。如Iterator接口,它定义了hasNext、next,还定义了remove方法,由于用户通常不会使用该方法,remove方法常被忽略。因此,实现Interator接口的类通常会为remove方法放置一个空的实现,这些都是些无用的模板代码。

    采用默认方法之后,你可以为这种类型的方法提供一个默认的实现,这样实体类就无需在自己的实现中显式地提供一个空方法。在Java 8中,Iterator接口就为remove方法提供了 一个默认实现。通过这种方式,可以减少无效的模板代码。实现Iterator接口的每个类无需再声明一个空的remove方法,因为它已经有一个默认的实现。

    2、行为的多继承

    单继承和多继承的比较

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

    ArrayList类

    \bullet 类型的多继承

    上例中ArrayList继承了一个类,实现了六个接口。因此ArrayList实际是七个类型的直接子类,分别是:AbstractList、List、RandomAccess、Cloneable、Serializable、 Iterable和Collection。在某种程度上,这就是类型的多继承。

    Java 8中接口方法可以包含实现,类可以从多个接口中继承它们的行为(即实现的代码)。

    \bullet 利用正交方法的精简接口

    示例:定义多个具有不同特质的形状。有的形状需要调整大小,但不需要有旋转的功能;有的需要能旋转和移动,但不需要调整大小。

    定义Rotatable接口,并提供两个抽象方法setRotationAngle和getRotationAngle:

    public interface Rotatable {

        void setRotationAngle(int angleInDegrees);

        int getRotationAngle();

        default void rotateBy(int angleInDegrees){

            setRotationAngle((getRotationAngle () + angle) % 360);

        }

    }

    实现了Rotatable的所有类都需要提供setRotationAngle和getRotationAngle的实现,同时它们也会继承rotateBy的默认实现。

    定义两个接口Moveable和Resizable,它们都包含了默认实现。:

    public interface Moveable {

        int getX();

        int getY();

        void setX(int x);

        void setY(int y);

        default void moveHorizontally(int distance){setX(getX() + distance);}

        default void moveVertically(int distance){setY(getY() + distance);}

    }

    public interface Resizable {

        int getWidth();

        int getHeight();

        void setWidth(int width);

        void setHeight(int height);

        void setAbsoluteSize(int width, int height);

        default void setRelativeSize(int wFactor, int hFactor){

            setAbsoluteSize(getWidth() / wFactor, getHeight() / hFactor);

        }

    }

    \bullet 组合接口

    通过组合这些接口,你可以创建不同的实体类。比如Monster可以移动、旋转和缩放。

    public class Monster implements Rotatable, Moveable, Resizable { … }

    Monster类会自动继承Rotatable、Moveable和Resizable接口的默认方法。

    Monster m = new Monster(); //构造函数会设置Monster的坐 标、高度、宽度及默认仰角

    m.rotateBy(180); //调用由Rotatable中继承的rotateBy方法

    m.moveVertically(10); //调用由Moveable中继承的moveVertically方法

    声明另一个Sun类,它要能移动和旋转,但是不能缩放。

    public class Sun implements Moveable, Rotatable { … }

    多种行为的组合

    通过精简的接口,你能获得最有效的组合,因为你可以只选择你需要的实现。

    --参考文献《Java8实战》

    相关文章

      网友评论

          本文标题:Java8学习笔记之默认方法

          本文链接:https://www.haomeiwen.com/subject/tfuvrctx.html