Effective-java-读书笔记之方法

第49条 检查参数的有效性

方法的参数限制, 应该在文档中指明, 并且在方法体的开头处检查参数, 以强制施加这些限制.

对于公有的方法, 要用Javadoc的@throws标签在文档中说明违反参数值限制时会抛出的异常.

Java 7新增了方法Objects.requireNonNull, 很好用.
Java 9新增了checkFromIndexSize, checkFromToIndex, 和checkIndex.

非公有的方法通常应该使用断言(assertion)来检查它们的参数. 如果断言失败会抛出AssertionError, 如果它们没有起到作用, 本质上也不会有成本开销.
(除非通过将-ea或者-enableassertions标记传递给Java解释器来启用它们.)

第50条 必要时进行保护性拷贝

你的类是否能够容忍对象进入数据结构之后发生变化? 如果答案是否定的, 就必须对该对象进行保护性拷贝, 并且让拷贝之后的对象而不是原始对象进入到数据结构中.

在内部组件被返回给客户端之前, 对它们进行保护性拷贝也是同样的道理.

如果参数的类型是可以被不被信任的人子类化的, 那么对参数进行保护性拷贝的时候, 不要使用clone方法.

只要有可能, 都应该使用不可变的对象作为对象内部的组件, 这样就不必再为保护性拷贝操心.

如果拷贝的成本受到限制, 并且类信任它的客户端不会不恰当地修改组件, 就可以在文档中指明客户端的职责是不得修改受到影响的组件, 以此来代替保护性拷贝.

第51条 谨慎设计方法签名

API设计技巧:

  • 谨慎选择方法名.
  • 不要过于追求提供便利的方法.
  • 避免过长的参数列表. -> 1.分解成多个方法; 2.创建辅助类, 用来保存参数的分组; 3.从对象构建到方法调用都采用Builder模式.
  • 参数类型优先使用接口而不是类.
  • 对于boolean参数, 要优先使用两个元素的枚举类型.

第52条 慎用重载

对于重载(overload)方法的选择是静态的, 而对于被覆盖(override)方法的选择则是动态的.

选择被覆盖方法的正确版本是在运行时进行的, 选择的依据是被调用方法所在对象的运行时类型.
所以子类方法与基类签名相同, 则覆盖基类, 尽管对象声明为基类, 但是调用时用的是子类的实现.

但重载的选择工作是在编译时进行的, 完全基于参数的编译时类型. 这样的代码很容易使人感到困惑.

安全而保守的策略是: 永远不要导出两个具有相同参数数目的重载方法. 如果方法使用可变参数(varargs), 保守的策略是不要重载它.

这项限制并不麻烦, 因为你始终可以给方法起不同的名称而不使用重载机制.

对于构造器, 没有选择不同名称的机会, 在许多情况下, 可以选择导出静态工厂.

当然如果对于每一种重载方法, 至少有一个对应的参数在两个重载方法中具有根本不同的类型, 就不会产生迷惑. 在这种情况下主要的混淆根源就消除了.

注意泛型和自动装箱会引起的歧义性, 举例 -> List<E>remove(E)remove(int).

Java 8引入的lambda和方法引用更增加了可能会引起重载解析歧义的可能性. -> 重载方法中, 不要在同样的参数位置接受不同的函数式接口.

第53条 慎用可变参数

可变参数机制通过先创建一个数组, 数组的大小为在调用位置所传递的参数数量, 然后将参数传到数组中, 最后将数组传递给方法.

在重视性能的情况下, 使用可变参数机制要特别小心.

在定义参数数目不定的方法时, 可变参数是一种很方便的方式, 但是它们不应该被过度滥用.

第54条 返回零长度的数组或集合, 而不是null

返回类型为数组或集合的方法, 应该返回一个零长度的数组或者集合, 没理由返回null. -> 不好用, 容易出错, 没有性能优势.

开销考虑:

  • 在这个级别上担心性能问题是不明智的, 除非分析表明这个方法是造成性能问题的真正源头.
  • 对于不返回任何元素的调用, 每次都返回同一个零长度数组是有可能的. (例如: Collections.emtpySet).

第55条 明智地返回optionals

在Java 8之前, 当一个方法无法返回值的时候有两种选择: 返回null或者抛出异常.
Java 8推出了一个新的解决方案: Optional<T>: 不可变容器, 含有一个或0个值.

Optional的精神和checked exception一样, 强迫用户意识到返回值有可能是为空的.
例子:

// Using an optional to provide a chosen default value 
String lastWordInLexicon = max(words).orElse("No words...");

// Using an optional to throw a chosen exception
Toy myToy = max(toys).orElseThrow(TemperTantrumException::new);

// Using optional when you know there’s a return value 
Element lastNobleGas = max(Elements.NOBLE_GASES).get();

如果默认值的计算比较重, 可以用orElseGet方法.
还有很多方法供各种用途: filter, map, flatMap, ifPresent等.

如果各种方法都不能满足你的需要, isPresent方法是一个很方便的方法, 它有值返回true, 无值返回false, 可以用来实现各种需求.
(不过通常可以用上面的各种方法更加优雅地解决问题.)

也不是所有的类型都可以从Optional受益, 容器类型(collections, maps, streams, arrays)和optionals不应该再用Optional包装.

Optional也不适用于性能关键的情形.

不要用Optional包装基本类型的装箱类型.
对基本类型的装箱类型返回Optional是有性能开销的, 还不如直接返回基本类型的默认值0.
库提供了OptionalInt, OptionalLongOptionalDouble.

通常, 用optional作为key, value或者集合中的元素都是不合适的, 会造成不必要的复杂性.
把optional保存在字段中也通常是一个bad smell. 但是也有例外, 比如想要合理地表达absence.

第56条 为所有导出的API元素编写文档注释

Javadoc可以根据源代码自动生成API文档.

要正确地为API建立文档, 就必须在每个导出的类, 接口, 构造函数, 方法和字段声明之前加上doc注释.

方法的文档注释应该简洁地描述出它和客户端之间的约定. 这个约定应该说明这个方法做了什么, 而不是如何完成这项工作的.

方法的文档注释还应该列举出:

  • 所有前提条件. 一般可以利用@throws, @param.
  • 后置条件.
  • 副作用.
  • 线程安全性.
  • 每个参数: @param 名词短语.
  • 返回值: @return 名词短语. (除非和方法描述一致时, 可根据所遵循的规定省略.)
  • 每个异常: @throws 含if的名词短语.

按惯例, @param, @return, @throws后面的短语或句子都不用句点来结束.

{@code}用来标记代码, 多行代码要加上<pre>标签, 变成: <pre>{@code xxx}</pre>.
注意代码中的注解符号@需要被省略.

按照惯例, 方法的文档注释中的"this"指代的是当前的对象.

Java 8新增@implSpec: 描述方法和子类的关系.
Java 9中Javadoc utility会忽略@implSpec, 除非你在命令行加上"implSpec🅰️Implementation Requirements:"

如果文档中要用HTML中的元素, 比如<, >&, 需要加上{@literal}标签.

文档注释必须在代码和生成文档中都保证可读性, 如果不能两者都保证, 生成文档的可读性优先.

每个文档注释的第一句话成了该注释所属元素的概要描述(summary description).
为了避免混乱, 在类或者接口中不应该有两个成员或者构造方法有相同的概要描述. 尤其要注意方法重载.

对于方法和构造器而言, 概要描述应该是个完整的动词短语, 它描述了该方法所执行的动作.
对于类, 接口和域, 概要描述应该是一个名词短语.

Java 9引入了index, 方面文档查询. 偶尔你需要用{@index}加入额外的index.

泛型, 枚举, 注解都需要额外的注意:

  • 当为泛型方法写文档时, 需要为每个泛型参数写文档注释.
  • 枚举需要为每个常量写注释.
  • 注解需要注释每个成员. (注解的概要描述是个动词短语.)

包级文档注释: package-info.java.
模块级文档注释: module-info.java.

在文档中还应该标明:

  • 线程安全性 -> 不论是否线程安全.
  • 如果可序列化, 需标明序列化形式.

Javadoc可以继承方法注释.
你可以用{@inheritDoc}标签来继承部分文档注释. (tricky and has some limitations).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

特特专属

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值