java8增加的接口中默认方法

前言

最近在工作中的一次小修改让自己应用到了java8中的新特性:接口默认方法,这里去简单记录下。在java8之后可以在接口定义方法的实现,成为default方法,类似于Scala中的trait。比如在Iterable接口中新增了foreach默认方法:

/**
 * Performs the given action for each element of the {@code Iterable}
 * until all elements have been processed or the action throws an
 * exception.  Unless otherwise specified by the implementing class,
 * actions are performed in the order of iteration (if an iteration order
 * is specified).  Exceptions thrown by the action are relayed to the
 * caller.
 *
 * @implSpec
 * <p>The default implementation behaves as if:
 * <pre>{@code
 *     for (T t : this)
 *         action.accept(t);
 * }</pre>
 *
 * @param action The action to be performed for each element
 * @throws NullPointerException if the specified action is null
 * @since 1.8
 */
default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

这个default方法的主要目的是为java8的Lambda表达式提供支持,如果将这个方法定义为普通接口方法,则会对现有的JDK的其他使用Iterable接口的类造成影响,因此提供了default方法的功能。

工作中的例子

有一个小需求是对一个表的插入的实体的name字段做一个处理,这里name字段如果为空则用手机号(默认手机号不为空)进行插入。因为调用这个insert方法的业务代码比较多,每个都去做这个逻辑会显得很麻烦并且很重复。所以就想到了直接在mapper的xml中去进行修改:

insert into table_name (
		<if test="name ==null or name == ''">NAME,</if> 
) values(
        <if test="name == null or name == ''">#{mobile}</if>
)

这样确实能够实现我们这个小需求,但是这个insert的mapper.xml是通过代码工具自动生成的标准insert方法,并且这样写可读性也不好,给人一种很奇怪的感觉。

这时候就用到了默认方法。这里我们可以在接口中定义一个默认方法insert,然后将之前的insert方法更换名称,在默认方法中去调用更换之后插入方法,而在默认方法中去做如果name为空则用手机号去代替这个逻辑。

int insertDefault(Clues entity);

/**
 *
 * @param entity
 * @return
 */
default int insert(Clues entity) {
    if (StringUtils.isEmpty(entity.getName()) && StringUtils.isNotEmpty(entity.getMobile())) {
        entity.setName(entity.getMobile());
    }
    return insertDefault(entity);
}

这样原来调用的insert方法也不需要去做更改,并且也不用在xml中进行改动就实现了这个小逻辑。注意要将xml中方insert方法改为insertDefault,这个更改比上边那种修改要显得合理的多。

默认方法的一个总结

java是面向对象的语言,那么就会有实现接口和继承父类,那么这些会对接口的默认方法有什么影响呢?下边参考博客:默认方法

存在一个父接口,定义了一个default方法:

public interface Parent {
    default String doit() {
        return "Parent";
    }
}

有一个类实现该接口,使用了默认的default方法:

public class ParentImpl implements Parent{

}

有一个ParentImpl2继承了ParentImpl,里面重写了接口中的默认方法:

public class ParentImpl2 extends ParentImpl {

    @Override
    public String doit() {
        return "ParentImpl2";
    }
}

有一个Child接口继承了Parent接口,并且重写了Parent接口中的默认方法:

public interface Child extends Parent {
    /**
     * 重写父接口中的default方法
     * @return
     */
    @Override
    default String doit() {
        return "Child";
    }
}

有一个ChildImpl实现了Child:

public class ChildImpl implements Child {
}

又有一个ChildImpl2继承了ParentImpl2也实现了Child接口,为了测试当子类实现的接口和继承的父类中都有默认方法的场景:

public class ChildImpl2 extends ParentImpl2 implements Child {
}

测试类:

public class Main {

    public static void main(String[] args) {
        /**
         * 测试实现类可以直接调用接口中的default方法
         */
        Parent parentImpl = new ParentImpl();
        // 将输出Parent
        System.out.println(parentImpl.doit());

        /**
         * 测试Child接口重写了Parent接口的default方法
         */
        Child child = new ChildImpl();
        // 将输出Child
        System.out.println(child.doit());

        /**
         * 测试ParentImpl2重写了Parent接口中default方法
         */
        Parent parentImpl2 = new ParentImpl2();
        // 将输出ParentImpl2
        System.out.println(parentImpl2.doit());

        /**
         * 测试ChildImpl2父类和实现的接口都有default方法,优先使用父类中定义的方法
         */
        Child childImpl2 = new ChildImpl2();
        // 将输出ParentImpl2
        System.out.println(childImpl2.doit());

    }
}

从上述测试结果可以看出:

  1. 实现类可以直接使用父接口中定义的default方法。

  2. 接口可以重写父接口中定义的default方法。

  3. 实现类可以重写父接口中定义的方法、

  4. 当父类和父接口都存在default方法时,使用父类中重写的default方法

特别的,如果一个类实现了两个接口,这两个接口中有同名的default方法签名时,此时会编译不通过,必须在子类中重写这个default方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值