方法
§ 检查参数的有效性
公有方法,要用 Javadoc 的 @throws
说明违反参数限制时的异常,如 IllegalArgumentException
、NullPointerException
等。
非公有方法,通常应该使用断言来检查它们的参数。断言如果失败,会抛出 AssertionError
。
1 | assert a != null; |
必要时进行保护性拷贝
1 | public class Period { |
如上所示,start 与 end 虽然使用了 final
,并且进行了参数约束,但是由于 Date
类本身是可变的。所以很容易违反这个约束:
1 | Date start = new Date(); |
从 Java 8 开始,解决此问题的方法是使用 Instant
(或 LocalDateTime
或 ZonedDateTime
)代替 Date
,因为 Instant
和其他 java.time 包下的类是不可变的。
Date
已过时,不应在新代码中使用。
为了保护 Period 实例的内部信息避免受到这种攻击,对构造器的每个可变参数进行保护性拷贝是必要的。修改后:
1 | public Period(Date start, Date end) { |
并且,对于参数类型可以被不可信任方子类化的参数,不要使用 clone
方法进行保护性拷贝。
§ 谨慎的设计方法签名
- 仔细选择方法名名称
- 不要过分地提供方便的方法。每种方法都应该“尽其所能”
- 避免过长的参数列表
- 对于参数类型,优先选择接口而不是类
- 考虑使用两个元素枚举类型代替布尔型,除非布尔型参数的含义在方法名中是明确的。枚举类型使代码更容易阅读和编写,还可以方便地在以后添加更多选项。
§ 谨慎的使用重载
以下代码,当我们使用 HashSet
、ArrayList
、HashMap
调用 classify
方法时,会打印 Unknown Collection 三次:
1 | public class CollectionClassifier { |
因为,要调用哪个重载,是在 编译时 做出的决定。对于 HashSet
、ArrayList
、HashMap
,参数编译时类型都是相同的:Collection<?>
对于重载方法的选择是静态的,对于被覆盖的方法选择是动态的。
§ 谨慎的使用可变参数
Java 1.5 开始增加了可变参数。
1 | static int sum (int... args) { |
这个对于方法接受 一个或多个参数 是合适的。但是对于 零或更多 是不合适的。
即使是在代码中进行检查,但是这样是运行时失败,而不是编译时失败。
通常可以提供两个参数解决:
1 | static int sum (int firstArg, int... args) { |
§ 返回零长度的数组或者集合,而不是 null
永远不要返回 null 来代替空数组或集合,通常客户端都需要进行 null 来进行检查,很容易出错。
1 | public List<Cheese> getCheeses() { |
如果有证据表明分配空集合会损害性能,可以通过重复返回相同的不可变空集合来避免分配,但是请记住,这是一个优化,很少需要它:
1 | Collections.emptyList() |
永远不要返回 null,而是返回长度为零的数组:
1 | private static final Cheese[] EMPTY = new Cheese[0]; |
§ 谨慎的返回 Optional
在 Java 8 之前,编写特定情况下无法返回任何值的方法时,要么抛出异常,要么返回 null(假设返回类型是对象或引用类型)。这两种方法都不完美。(抛出异常代价很高,因为在 创建异常时会捕获整个堆栈跟踪)
Optional<T>
表示一个不可变的容器,它可以包含一个非 null 的 T
引用,也可以什么都不包含,不包含任何内容的 Optional
被称为空。
1 | public static <E extends Comparable<E>> Optional<E> max(Collection<E> c) { |
如何选择返回 Optional
而不是返回 null 或抛出异常呢? Optional
在本质上类似于检查异常。
如果可能无法返回结果,并且在没有返回结果,客户端还必须执行特殊处理的情况下,则应声明返回 Optional
。
1 | String lastWordInLexicon = max(words).orElse("No words..."); |
并不是所有的返回类型都能从
Optional
的处理中获益。容器类型,包括集合、映射、Stream、数组和Optional
,不应该封装在Optional
中。与其返回一个空的Optional
,不还如返回一个空的List<T>
。
- Optional 是必须分配和初始化的对象,从 Optional 中读取值需要额外的迂回。 这使得 Optional 不适合在某些性能关键的情况下使用
- 永远不应该返回装箱的基本类型的 Optional。而考虑使用
OptionalInt
、OptionalLong
等。
大多数其他
Optional
的用法都是可疑的。例如,永远不要将Optional
用作映射值等等。
§ 为公开的API编写文档注释
- 文档注释在源代码和生成的文档中都应该是可读的通用原则
- 类或接口中的两个成员或构造方法不应具有相同的概要描述
- 记录泛型类型或方法时,请务必记录所有类型参数
- 在记录枚举类型时,一定要记录常量,以及类型和任何公共方法
- 在为注解类型记录文档时,一定要记录任何成员
- 无论类或静态方法是否线 程安全,都应该在文档中描述其线程安全级别