这部分是关于的Java8在接口中定义默认方法和静态方法的。实话说,我一时没看出来这个和Lambda表达式有什么关系,也不知道为什么书上会安排在同一章,我还是拆来来吧。

默认方法

同样在Java8以前,接口当中是不会存在任何方法实现的,在某种程度上可以看做纯抽象的的方法。

这就有一个问题,想象有一个接口I,经过若干次迭代,它已经有了A、B、C、D……N个实现类,突然有一天,接口I中不得不新增一个方法,然后它的N个实现类要一一实现这个新的方法吗?可不可以让接口中这个新的方法提供一个默认实现呢?

恐怕我们很容易联想到早些年AWT中的事件模型,例如窗口事件java.awt.event.WindowListener,它有7个方法未实现,而很可能我们只需要点击关闭窗口时弹出一个确认对话框一个功能……于是就有了java.awt.event.WindowAdapter这么个东西,把每个方法都做了一个空的实现。这样做当然不是不可以,但利用Java8的默认方法要简单不少,用default关键字声明默认方法即可,至少不用定义Adapter类了。

1
2
3
4
5
public interface Lived {
default String getDescription() {
return "有生命的";
}
}

不过新的问题又出现了,Java是允许接口多继承的,万一接口A、B都对某个共享方法X提供了默认实现,偏偏接口C又同时继承了接口A、B怎么办?是不是突然有种当年学C++时解决菱形继承的既视感?莫方,Java8对这种问题的解决办法简单粗暴——开发人员自己决定!也就是说,你必须写代码来决定,究竟是自己再重写一次方法X,还是从A、B的实现中选一个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface Named {
default String getDescription() {
return "有名字的";
}
}
public class Human implements Lived, Named {
//必须重写冲突的getDescription方法
@Override
public String getDescription() {
return "人类";
//或可以指定某个父接口的默认实现
//return Lived.super.getDescription();
}
}

再换一种情况,假设类C继承了类B又实现了接口A,偏偏接口A对方法X提供了默认实现,类B覆盖了这个实现,在C不重写方法X的情况下,生效的将会是类B中的实现,接口A中的默认实现会被忽略,这称为“类优先”原则。

这就有问题了,而且我个人感觉不是很容易被察觉到:接口也是一种特殊的类,它也是继承Object类的,根据类优先原则,我们就永远不可能为Object类中的方法定义默认实现。

静态方法

不知道你有没有被诸如Collection/Collections这样长得贼像的“兄弟”坑过呢?反正我是有,有时候着急了,或者敲快了,还对着错误一头雾水的。其实,这两家伙,前者是接口,后者是类,而且后者为前者提供一些工具方法或者工厂方法。

Java8开始,不仅允许在接口中添加默认方法,还允许添加静态方法了!不知道可不可以认为像Collections这样的类没有太大用处了呢?

允许这么做是有原因的。即使是默认方法,也需要通过Lambda表达式实例化函数式接口,或者某个实现了这个接口的类的实例才可以访问到,这让静态工厂方法们情何以堪啊?当然下面这个例子有点随意了=.=

1
2
3
4
5
public interface Lived {
static boolean instanceOf(Object obj) {
return obj instanceof Lived;
}
}