Java SE 8新特性 - 默认方法
阅读:106Java SE 8 新添加了默认方法的特性,我们首先看下官方描述:
Default methods enable new functionality to be added to the interfaces of libraries and ensure binary compatibility with code written for older versions of those interfaces.
意思是,可以添加默认方法到现有的库中,确保与采用旧版本接口编写的代码的二进制兼容性。
1.默认方法的定义
在接口中,可以通过default关键字,定义一个默认方法。比如我们定义一个Animal的接口,有两个默认方法,name和run,代码如下:
package net.zhifou.base.defaule;
public interface Animal {
default String name() {
return getClass().getName() + "_" + hashCode();
}
default void run() {
System.out.println("Animal can run...");
}
}
2.为什么需要默认方法?
在Java SE 8之前,实现类和接口耦合性比较高。如果我们想给一个接口添加一个新特性,那么实现了该接口的类都需要实现该特性,显然代价太大。那么有了默认方法,我们就可以在接口把该特性声明为默认方法,这样,所有实现了该接口的类,即拥有了该特性,且不需要修改代码。所以,可以轻松实现接口backward compatibility(向后兼容)。
在Java SE 8中lambda表达式,很多新特性都是通过默认方法实现。比如为List实现forEach特性(遍历List),java.util.ArrayList的顶层接口java.lang.Iterable,就是添加了forEach这个默认方法,从而轻松实现了该特性,所有实现了java.lang.Iterable接口的类,不需要修改代码,轻松拥有forEach特性。
在java.lang.Iterable,forEach源码如下:
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
试想下,在java.lang.Iterable接口中添加forEach(非默认方法),完啦,所有的实现了该接口的类都需要修改源码,不然编译会报AbstractMethodError这个错误。
3.默认方法带来的冲突问题
假如接口中定义了一个默认方法,然后在超类或者其他接口又定义了同样的方法,会发生情况呢?这个会产生二义性。在其他语言中,比如C++和Scala,消除这些二义性,需要定义非常复杂的规则。庆幸的是,Java的规则非常简单,规则如下:
1)超类优先。
2)接口冲突。
下面我们详细的解释下。
3.1 超类优先
如果一个类的超类和接口同时具有相同的方法,这里指同名和方法参数类型相同,那么超类优先。
首先定义一个超类Pig:
package net.zhifou.base.defaule;
public class Pig {
public String name() {
return "My name is PeiQi.";
}
}
再定义PigA,继承超类Pig,实现Animal,代码如下:
package net.zhifou.base.defaule;
public class PigA extends Pig implements Animal {
public static void main(String[] args) {
PigA pigA = new PigA();
System.out.println(pigA.name());
}
}
运行PigA,结果如下:
My name is PeiQi.
这里验证了超类优先,实际执行是超类的方法,接口的默认方法被忽略了。超类优先,或者叫做“类优先”,该原则可以确保与 Java SE 7 的兼容性,在添加默认方法之前的代码不受影响。
3.2 接口冲突
如果超接口提供了一个默认方法,另外接口提供了相同的方法(指同名和方法参数类型相同),那么会产生二义性,这时候,实现两个接口的类必须覆盖形同方法类来消除二义性。
我们定义一个Mouse的接口,定义相同的默认方法name:
package net.zhifou.base.defaule;
public interface Mouse {
default String name() {
return "My name is Mickey.";
}
}
现在我们Mickey的类,同时实现了Animal和Mouse,代码如下:
package net.zhifou.base.defaule;
public class Mickey implements Mouse, Animal {
}
此时编译器会报错误,需要消除二义性。那么怎么消除呢?只需要实现name方法,在name方法中选择其中一个冲突方法即可。修改后的如下:
package net.zhifou.base.defaule;
public class Mickey implements Mouse, Animal {
@Override
public String name() {
return Mickey.class.getName();
}
}
这里注意,一定不要通过默认方法覆盖Object
类的 equals
、hashCode
和 toString
方法,因为“类优先”原则,所以覆盖无效。
总结:
1)接口的静态方法,是为了消除伴随类,静态方法必须是public的,关键词public可以省略,static不可以省略。
2)接口的默认方法,是用default修饰的方法。目的是为了向后兼容,可以轻松扩展现有接口。
© 版权归知否网(zhifou.net)所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权知否网将依法追究其法律责任。
读后有收获,请作者喝杯咖啡
