0%

Java语言新特性漫谈:Java 8 之接口篇

本文是《Java语言新特性漫谈》系列文章中的一篇,该系列文章主要探讨各 Java 版本的语言特性方面的增强更新。

对于接口类型而言,Java 8 中最大的改进在于支持在接口中声明默认方法静态方法

接口默认方法

什么都别说,先上语法:

1
2
3
4
5
public interface MyInterface {
default void defaultMethod() {
// 默认方法
}
}

显示,默认方法重用了 default 关键字,以表明该方法是一个接口默认方法,可实现方法体。

从之前的版本来看,Java 使用接口规避了多继承带来的问题,但同时引入了另一个问题:接口难以演进!

试想,如果要给一个接口添加一个方法,会发生什么?

所有的实现类都要实现该方法,因此,涉及所有实现类的修改,但并非所有实现类都在我们的控制下,很难做到全修改。另一方面,新接口与已分发的老版本包存在兼容性问题,这显然与 Java 向下兼容的理念背道而驰。

因此,为了利于接口的演进,为接口增加默认方法特性应该是相对较为简单的解决方案了。

接口在添加新方法时就已经提供了方法实现,故而就不需要修改所有实现类以实现新增方法。

皆大欢喜了吗?不不不,想想多继承的问题。如果接口有默认方法,那么这跟类就很相似了,那么一个类实现多个有同名默认方法的接口时,到底“继承”的是哪个父接口的默认方法?

Java 的处理方式是:必需重写默认方法。当然,可以选择重写所有代码,也可以以 SuperInterface.super.method() 的形式调用某个父接口的默认方法。

多重继承带来的问题:二义性,也叫菱形问题;解决办法就是子类尽量覆写默认方法并显式声明调用哪个方法。

另外,既然叫“默认方法”,故而一旦有了实现,默认方法就无效了。该实现可以是当前类重写,也可以继承自父类。

因此,应该意识到,试图为接口添加 Object 中已有方法为默认方法会导致编译错误。比如,想要为接口添加 toString() 方法。

接口静态方法

依旧先看代码:

1
2
3
4
5
public interface StaticInterface {
static void staticMethod() {
// 静态方法
}
}

接口中声明的静态方法与普通的静态方法没有本质区别,也是使用类似 Interface.staticMethod() 的方式调用。

而接口支持静态方法给了我们编写“工具类”提供了另一种思路。编写一个只提供静态方法的工具类,并不算是严格意义上的面向对象编程。而另一方面,为了使用者不“意外”地创建工具类对象,通常还要将构造器私有化。一个更“优雅”的方式是使用抽象类,以避免意外实例化,而抽象类本身却是暗示使用者应该派生它。总而言之,工具类的实现,无论是使用一个普通类,还是抽象类,都有些许瑕疵,而使用接口则是一种更为“纯粹”的方式。

另一个好处是,接口的实现实体与其辅助方法之间不再彼此孤立的。因此,使用者不再需要臆测某个对象是否有对应的辅助工具类,而只需查看其接口即可。

关于 Python 的一些联想

如果对 Python 有一些了解的话,应该知道 Python 的方法分为三种类型:实例方法、类方法和静态方法。呃……大家知道,对于 Java 而言,类方法和静态方法是同一概念。所以大体上说,Java 的实例方法和类/静态方法分别跟 Python 的实例方法和类方法是对等的。

但是,所谓的 Python 静态方法相当于放置在类中的函数,它跟类本身没有直接关系,仅仅是将所在类当作命名空间而已,Java 中没有类似的对应物。不过,当 Java 引入接口静态方法后,我们可以认为作为“工具方法”的接口静态方法与 Python 中的静态方法是类似的概念了。