1.6 接口中的静态方法
问题
用户希望为接口添加一个类级别(class level)的工具方法和相应的实现。
方案
将接口方法声明为 static,并以常规方式添加实现。
讨论
Java 类的静态成员是类级别的。换言之,静态成员与整个类相关联,而不是与特定的实例相关联。但从设计的角度看,静态成员在接口中的使用是有问题的,举例如下。
- 当多个不同的类实现接口时,类级别成员指的是什么?
- 类是否需要通过实现接口来使用静态方法?
- 类中的静态方法是通过类名访问的。如果类实现了一个接口,那么静态方法是通过类名还是接口名来调用呢?
为解决这些问题,Java 开发团队尝试了几种不同的方案。
在 Java 8 之前,接口完全不支持使用静态成员,不过这导致了工具类的产生,这是一种只包含静态方法的类。java.util.Collections 就是一种典型的工具类,它不仅包括用于排序和搜索的方法,也定义了采用同步或不可修改的类型包装集合的方法。在 NIO 包中,另一种工具类是 java.nio.file.Paths,它只包括从字符串或 URI 中解析 Path 实例的两个静态方法。
而在 Java 8 中,我们可以随时为接口添加静态方法,步骤如下。
- 为方法添加
static关键字。 - 提供一种无法被重写的实现。此时,静态方法类似于默认方法,包含在 Javadoc 的 Default Methods(默认标签)中。
- 通过接口名访问方法。类不需要通过实现接口来使用静态方法。
java.util.Comparator 接口定义的 comparing 方法就是一种实用的静态方法,它包括 comparingInt、comparingLong、comparingDouble 等三种基本变体。此外,Comparator 接口还包括 naturalOrder 和 reverseOrder 两种静态方法。例 1-28 给出了这些方法的用法。
例 1-28 字符串排序
List<String> bonds = Arrays.asList("Connery", "Lazenby", "Moore","Dalton", "Brosnan", "Craig");List<String> sorted = bonds.stream().sorted(Comparator.naturalOrder()) ➊.collect(Collectors.toList());// [Brosnan, Connery, Craig, Dalton, Lazenby, Moore]sorted = bonds.stream().sorted(Comparator.reverseOrder()) ➋.collect(Collectors.toList());// [Moore, Lazenby, Dalton, Craig, Connery, Brosnan]sorted = bonds.stream().sorted(Comparator.comparing(String::toLowerCase)) ➌.collect(Collectors.toList());// [Brosnan, Connery, Craig, Dalton, Lazenby, Moore]sorted = bonds.stream().sorted(Comparator.comparingInt(String::length)) ➍.collect(Collectors.toList());// [Moore, Craig, Dalton, Connery, Lazenby, Brosnan]sorted = bonds.stream().sorted(Comparator.comparingInt(String::length) ➎.thenComparing(Comparator.naturalOrder())).collect(Collectors.toList());// [Craig, Moore, Dalton, Brosnan, Connery, Lazenby]
❶ 自然顺序(字典序)
❷ 反向顺序(字典序)
❸ 按小写名称排序
❹ 按姓名长度排序
❺ 按姓名长度排序,如果长度相同则按字典序排序
本例显示了如何利用 Comparator 接口提供的静态方法对一份演员名单进行排序,名单中出现的演员是这些年来詹姆斯 • 邦德的扮演者。9 有关比较器的详细讨论,请参见范例 4.1。
9我差点将伊德瑞斯 • 艾尔巴(Idris Elba)加入名单,但总算忍住没这么做。(艾尔巴是英国影星,因在《环太平洋》《雷神》等影片中饰演的角色为影迷所熟知,2014 年曾传出他将出演下一任邦德的消息——译者注。)
由于接口中有静态方法,我们不必创建单独的工具类。但需要的话,仍然可以创建工具类。
请注意以下几点:
- 静态方法必须有一个实现
- 无法重写静态方法
- 通过接口名调用静态方法
- 无须实现接口以使用静态方法
另见
接口中静态方法的应用贯穿全书,有关 Comparator 接口定义的静态方法请参见范例 4.1。
