Java8新特性之默认方法

传统上,Java程序的接口是将相关方法按照约定组合到一起的方式。 实现接口的类必须为接口中定义的每一个方法提供一个实现,或者从父类中继承它的实现。所以,一旦类库的设计者需要更新接口,向其中加入新的方法,就会出现问题。现实情况是,现存的实体类往往不在接口设计者的控制范围之内,这些实体类为了适配新的接口约定也需要进行修改。由于Java8的API在现存的接口上引入了非常多的新方法,这种变化带来的问题也愈加严重,一个例子就是List接口上新加的sort方法,会导致实现List接口的类中都需要添加sort方法。
一.默认方法
Java8为了解决这一问题引入了一种新的机制。Java8中的接口现在支持在声明方法的同时提供实现,通过两种方式完成这种操作。其一,Java8允许在接口内声明静态方法。其二,Java8引入了一个新功能叫 默认方法,通过默认方法可以指定接口方法的默认实现。因此实现接口的类如果不显示地提供该方法的具体实现,会自动继承默认实现
List接口中的默认sort方法如下:
default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays. sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a ) {
            i.next();
            i.set((E) e);
        }
    }
默认方法的引入就是为了以兼容的方式解决像Java API这样的类库的演进问题。简而言之,向接口添加方法是诸多问题的罪恶之源,一旦接口发生变化,实现这些接口的类往往也需要更新,提供新添方法的实现才能适配接口的变化。
二.Java8的抽象类和抽象接口
接口中可以提供方法的实现,这看起来接口和抽象类好像没什么区别了,那么Java8中的抽象类和抽象接口之间的区别是什么呢?
首先,一个类只能集成一个抽象类,但是一个类可以实现多个接口。
其次,一个抽象类可以通过实例变量保存一个通用状态,而接口是不能有实例遍历的。
说到这里,就想起一个常问的问题,抽象类和接口的区别是什么?很多人从书上看到抽象类中可以有实现,接口中不能有方法实现等一串语法上的区别,那么真正的使用上有什么区别呢,什么时候用抽象类,什么时候用接口呢?其实,抽象类更像是一个模板,当发现一些类中存在共性,或者都属于某个抽象时,可以使用抽象类来做模板。而接口其实是一个约定,用来和外部通信的。 抽象类和它的子类之间应该是一般和特殊的关系,而接口仅仅是它的子类应该实现的一组规则。举个使用抽象类的例子,交通工具定义成抽象类,飞机,轮船,火车则为交通工具的实现类,而一些实现接口的类只是说明这些类满足这项规则,如实现Serilizable,Comparable等接口。
三.解决冲突的规则
如果一个类同时实现了两个接口,这两个接口恰巧又提供了同样的默认方法签名,这时会发生什么情况?类会使用哪个方法?虽然在实际开发中这种冲突极少发生,不过一旦发生这种情况,必须要有一套规则来处理这些冲突。
解决问题的三条规则
1.类中的方法优先级最高。类或父类中声明的方法的优先级高于任何声明为默认方法的优先级。
2.如果无法根据第一条进行判断,那么子接口的优先级最高。即如果B继承了A,那么B的优先级比A高。
3.如果还是无法判断,必须通过显示覆盖和调用期望的方法,显示地选择使用哪一个默认方法的实现。
下面举几个例子说明上面三条规则
interface A{
     default void hello(){
          System. out.println("Hello from A" );
     }
}
interface B extends A{
     default void hello(){
          System. out.println("Hello from B" );
     }
}
class C implements A,B{
     
}

上面代码中接口B继承了接口A,C实现了接口A,B,因为A和B中都有默认方法hello,根据第二条规则,B的优先级更高,所以调用C的hello方法输出的是Hello from B。
class C extends D implements A,B{
     
}
class D implements A{
     public void hello(){
          System. out.println("Hello from D" );
     }
}

加个类D实现接口A,覆写hello方法,再用C继承D,根据第一条规则,类中的方法优先级最高,所以调用C的hello方法输出的是Hello from D。
冲突及显示的消除歧义
如果接口A和接口B中没有继承关系,就不能使用前两条规则解决冲突了,这时候我们需要显示消除歧义,覆盖C中的hello方法,在方法体内显示地调用希望调用的方法,否则会编译错误。
interface A{
     default void hello(){
          System. out.println("Hello from A" );
     }
}
interface B{
     default void hello(){
          System. out.println("Hello from B" );
     }
}
class C implements A,B{
     public void hello(){
          B. super.hello();
     }
}

总结:Java8提供的默认方法确实是非常强大的东西,不过我认为这也是一把双刃剑,向接口添加方法是诸多问题的罪恶之源,我们不能认为Java8提供了接口的默认方法实现,就可以任意的修改接口,理想状态下接口尽量不要改动。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值