Java 8 新特性:接口增强
1.接口增强
Java 8 对接口做了进一步的增强。
a. 在接口中可以添加使用 default 关键字修饰的非抽象方法。即:默认方法(或扩展方法)
b. 接口里可以声明静态方法,并且可以实现。
2.默认方法(或扩展方法)
Java 8 允许给接口添加一个非抽象的方法实现,只需要使用 default 关键字即可,这个特征又叫做扩展方法(也称为默认方法或虚拟扩展方法或防护方法)。在实现该接口时,该默认扩展方法在子类上可以直接使用,它的使用方式类似于抽象类中非抽象成员方法。
Note:扩展方法不能够重写(也称复写或覆盖) Object 中的方法,却可以重载Object 中的方法。
eg:toString、equals、 hashCode 不能在接口中被覆盖,却可以被重载。
默认方法允许我们在接口里添加新的方法,而不会破坏实现这个接口的已有类的兼容性,也就是说不会强迫实现接口的类实现默认方法。
默认方法和抽象方法的区别是抽象方法必须要被实现,默认方法不是。作为替代方式,接口可以提供一个默认的方法实现,所有这个接口的实现类都会通过继承得到这个方法(如果有需要也可以重写这个方法)
eg:
interface Defaulable { //使用default关键字声明了一个默认方法 @SuppressLint("NewApi") default String myDefalutMethod() { return "Default implementation"; } } class DefaultableImpl implements Defaulable { //DefaultableImpl实现了Defaulable接口,没有对默认方法做任何修改 } class OverridableImpl implements Defaulable { //OverridableImpl实现了Defaulable接口重写接口的默认实现,提供了自己的实现方法。 @Override public String myDefalutMethod() { return "Overridden implementation"; } }
Note:
JVM平台的接口的默认方法实现是很高效的,并且方法调用的字节码指令支持默认方法。默认方法使已经存在的接口可以修改而不会影响编译的过程。java.util.Collection中添加的额外方法就是最好的例子:stream(), parallelStream(), forEach(), removeIf()
虽然默认方法很强大,但是使用之前一定要仔细考虑是不是真的需要使用默认方法,因为在层级很复杂的情况下很容易引起模糊不清甚至变异错误。
优缺点:
可以在不破坏代码的前提下扩展原有库的功能。但从另一个方面来说,这使得接口作为协议,类作为具体实现的界限开始变得有点模糊。优点是,它通过一个很优雅的方式使得接口变得更智能,同时还避免了代码冗余,并且扩展类库。缺点是,个人估计很快就会看到有在接口方法里获取this引用然后强制转化成某个具体类型的写法了。
多重继承的冲突说明:
由于同一个方法可以从不同接口引入,自然而然的会有冲突的现象,规则如下:
1)一个声明在类里面的方法优先于任何默认方法
2)优先选取最具体的实现
eg:
B重写了A的hello方法:
输出结果:Hello World from B
小结:
默认方法给予我们修改接口而不破坏原来的实现类的结构提供了便利,目前java 8的集合框架已经大量使用了默认方法来改进了,当我们最终开始使用Java 8的lambdas表达式时,提供给我们一个平滑的过渡体验。也许将来我们会在API设计中看到更多的默认方法的应用。
参考默认方法官方文档地址:
http://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
3.接口的静态方法
接口里可以声明静态方法,并且可以实现。
private interface DefaulableFactory { // Interfaces now allow static methods static Defaulable create(Supplier< Defaulable > supplier ) { return supplier.get(); } }
4.接口增强示例
接口的静态方法和默认方法一起调用示例(结合上面的示例,在某类中的main()中演示……)
eg:
public static void main( String[] args ) { Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new ); System.out.println( defaulable.myDefalutMethod() ); defaulable = DefaulableFactory.create( OverridableImpl::new ); System.out.println( defaulable.myDefalutMethod() ); }
输出结果:
Default implementation
Overridden implementation
5.为什么不能用默认方法来重载equals,hashCode和toString?
接口不能提供对Object类的任何方法的默认实现。从接口里不能提供对equals,hashCode或toString的默认实现。因为若可以会很难确定什么时候该调用接口默认的方法。
如果一个类实现了一个方法,那总是优先于默认的实现的。一旦所有接口的实例都是Object的子类,所有接口实例都已经有对equals/hashCode/toString等方法非默认 实现。因此,一个在接口上的这些默认方法都是没用的,它也不会被编译。(简单地讲,每一个java类都是Object的子类,也都继承了它类中的equals/hashCode/toString方法,那么在类的接口上包含这些默认方法是没有意义的,它们也从来不会被编译。)
6.java 8中抽象类与接口的异同
相同点:
1)都是抽象类型;
2)都可以有实现方法(以前接口不行);
3)都可以不需要实现类或者继承者去实现所有方法,(以前不行,现在接口中默认方法不需要实现者实现)
不同点:
1)抽象类不可以多重继承,接口可以(无论是多重类型继承还是多重行为继承);
2)抽象类和接口所反映出的设计理念不同。其实抽象类表示的是"is-a"关系,接口表示的是"like-a"关系;
3)接口中定义的变量默认是public static final 型,且必须给其初值,所以实现类中不能重新定义,也不能改变其值;抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以重新赋值。
Note:friendly 型:如果一个类、类属变量及方法不以public,protected,private这三种修饰符来修饰,它就是friendly类型的,那么包内的任何类都可以访问它,而包外的任何类都不能访问它(包括包外继承了此类的子类),因此,这种类、类属变量及方法对包内的其他类是友好的,开放的,而对包外的其他类是关闭的。