Java8实战之默认方法
前言
在前面,我们学习了Lambda以及Stream,也体会到了这两者结合后所带来的方便性,尤其是Stream,有了Stream之后,对于容器的大部分操作都变得非常简单,只需要通过组合一系列的中间操作以及结束操作,就能筛选,转换,收集需要的对象,然而,这中间有一个问题,那就是似乎大部分的容器都支持stream()
这个方法,难道是所有的实现都进行了修改了吗?这肯定是不可能,一方面,修改现有的容器,为每个容器加入一个方法,是非常繁琐的操作;另一方面,如果我们想实现自己的容器,还要自己实现对应的stream()
方法?。但除了给每个子类加入方法,那就只能是在接口中加入,但在接口中加入,又似乎不合理,因为所有的实现类都必须实现该方法了,答案就在默认方法中。
默认方法
为了解决上面的矛盾,在Java8中加入了新的机制,默认方法,默认方法用关键字default
来标注,被default
所标注的方法,需要提供实现,而子类可以选择实现或者不实现该方法,通过这样的机制,就能够实现在接口中加入新方法,则子类无需进行任何改动,需要注意的是default
只能用于接口中修饰方法,不能在类中使用
interface DefaultMethodTest {
default void print() {
System.out.println("hello");
}
}
我们来看下容器中的stream
方法的是否如此
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
关于StreamSupport
这里就不展开了,等以后继续深入学习之后,再来研究它,主要是现在还没搞懂,囧。不过,通过JDK源码,我们确实看到了default stream()
方法
不过看到这里,可能又会有一个疑惑,Java中通过单继承机制来避免多继承所带来的各种问题,比如菱形继承问题,而在接口中引入默认实现,同时接口又支持多重实现,那么就会出现两个接口中有同样的方法,那这样子该怎么处理呢?
interface A {
default void print() {
System.out.println("interface A");
}
}
interface B {
default void print() {
System.out.println("interface B");
}
}
// 出现混乱
class C implements A, B {
}
Java8中采用了强制的手段,其规则如下
- 如果是继承一个类并且实现接口,两者具有相同的默认方法,以类为基准
- 如果是继承或者实现一个已经继承的接口,以子接口中的默认方法为准
- 如果此时依旧出现混乱,则必须显示进行处理(显示实现)
从上面的规则可以看到,其实就是以类为优先,如果是父类已经包含了默认方法,则以父类的为基准,如下
class D implements A {
@Override
public void print() {
System.out.println("class A");
}
}
class C extends D implements A {
public static void main(String[] args) {
new C().print(); // 输出 class A
}
}
interface D extends A {
@Override
default void print() {
System.out.println("interface D");
}
}
class C implements D, A {
public static void main(String[] args) {
new C().print(); // 输出 interface D
}
}
可以看到上面的情况是具有明显的继承形式的,并且是能够明显地区分出哪一个是最接近目标类的,所以就以该类中的实现为准
当无法明显地进行区分的时候,比如下面的例子,就需要强制实现了
class C implements A, B {
}
总结
Java8中通过在接口中引入默认实现的机制来实现在接口中加入新的方法,而子类无需变动,由于该机制会引入新的问题,也就是多个实现的时候,以哪一个为准,Java8中采用了强制的方法,只要无法区分哪一个是最接近目标类的,就强制目标类需要实现对应的方法。由于在一般情况下,我们是不需要使用到默认方法的,该方法虽然不是一个很好的解决方法,但却是一个不错的方法。