默认方法
实现接口的类必须为接口中定义的每个方法提供一个实现,或者从父类中继承它的实现。但是,一旦类库的设计者需要更新接口,向其中加入新的方法,这种方式就会出现问题。现实情况是,现存的实体类往往不在接口设计者的控制范围之内,这些实体类为了适配新的接口约定也需要进行修改。由于Java 8的API在现存的接口上引入了非常多的新方法,这种变化带来的问题也愈加严重,一个例子就是像Guava和Apache Commons这样的框架现在都需要修改实现了List接口的所有类,为其添加sort方法的实现。
Java 8为了解决这一问题引入了一种新的机制。Java 8中的接口现在支持在声明方法的同时提供实现!通过两种方式可以完成这种操作。
- Java 8允许在接口内声明静态方法。
- Java 8引入了一个新功能,叫默认方法,通过默认方法你可以指定接口方法的默认实现。
换句话说,接口能提供方法的具体实现。因此,实现接口的类如果不显式地提供该方法的具体实现,就会自动继承默认的实现。这种机制可以使你平滑地进行接口的优化和演进。实际上,到目前为止你已经使用了多个默认方法。
两个例子
List接口中的sort方法是Java 8中全新的方法,它的定义如下:
default void sort(Comparator<? super E> c){
Collections.sort(this, c);
}
Collection中的stream方法的定义如下
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
默认方法的引入就是为了以兼容的方式解决像Java API这样的类库的演进问题的
不断演进的API
为了理解为什么一旦API发布之后,它的演进就变得非常困难,
假设你是一个流行Java绘图库的设计者。
你的库中包含了一个Resizable接口,它定义了一个简单的可缩放形状必须支持的很多方法, 比如:setHeight、setWidth、getHeight、getWidth以及setAbsoluteSize。此外,你还提供了几个额外的实现(out-of-box implementation),如正方形、长方形。由于你的库非常流行,你的一些用户使用Resizable接口创建了他们自己感兴趣的实现,比如椭圆。
发布API几个月之后,你突然意识到Resizable接口遗漏了一些功能。比如,如果接口提供一个setRelativeSize方法,可以接受参数实现对形状的大小进行调整,那么接口的易用性会更好。
你会说这看起来很容易啊:为Resizable接口添加setRelativeSize方法,再更新Square和Rectangle的实现就好了。不过,事情并非如此简单!你要考虑已经使用了你接口的用户,他们已经按照自身的需求实现了Resizable接口,他们该如何应对这样的变更呢?
非常不幸,你无法访问,也无法改动他们实现了Resizable接口的类。这也是Java库的设计者需要改进Java API时面对的问题。
初始版本的API
public interface Resizable extends Drawable{
int getWidth();
int getHeight();
void setWidth(int width);
void setHeight(int height);
void setAbsoluteSize(int width, int height);
}
用户实现
用户根据自身的需求实现了Resizable接口,创建了Ellipse类:
public class Ellipse implements Resizable {
…
}
他实现了一个处理各种Resizable形状(包括Ellipse)的游戏:
public class Game{
public static void main(String...args){
List<Resizable> resizableShapes =
Arrays.asList(new Square(), new Rectangle(), new Ellipse());
Utils.paint(resizableShapes);
}
}
public class Utils{
public static void paint(List<Resizable> l){
l.forEach(r -> {
r.setAbsoluteSize(42, 42);
r.draw();
});
}
}
第二版API
库上线使用几个月之后,你收到很多请求,要求你更新Resizable的实现,让Square、Rectangle以及其他的形状都能支持setRelativeSize方法。为了满足这些新的需求,你发布了第二版API
public interface Resizable {
//...
void setRelativeSize