《Effective Java》-方法

64 阅读5分钟

方法

Item 49: Check parameters for validity

检查参数的有效性

大部分方法对参数有一定的限制,因此,需要在方法体的最开始去对参数进行校验。

如果没有对参数进行校验,可能导致违背失败原子性(failure atomicity)。

对于公有,或者受保护的方法,使用@throws来说明如果违反限制将会抛出的异常。对于包私有或者私有的方法,则需要使用断言来对参数进行校验。

Item 50: Make defensive copies when needed

必要时,进行保护性拷贝

使用类的客户端可能会尽其所能地去破坏这个类的约束条件,因此必须保护性地设计程序。如果没有对象的帮助,一个类不可能去修改对象内部的状态,但是对象很容易就破坏内部的状态。看下面这个例子:

public Period(Date start, Date end) { 
    if (start.compareTo(end) > 0) 
        throw new IllegalArgumentException( start + " after " + end);          
    this.start = start; 
    this.end = end; 
}

如果客户端通过构造器创建Period对象之后,修改end对象的值,很容易就破坏了Period的约束。为了避免这样的情况,需要对构造器的每个可变参数都进行保护性的拷贝。

public Period(Date start, Date end) { 
    this.start = new Date(start.getTime()); 
    this.end = new Date(end.getTime()); 
    if (this.start.compareTo(this.end) > 0) 
        throw new IllegalArgumentException( this.start + " after " + this.end); 
}

需要注意的是,保护性拷贝是在检查参数的有效性之前进行的,并且有效性检查时针对拷贝之后的对象,而不是针对原始的对象。这样做的好处就是,避免在检查参数到拷贝参数这样的危险阶段中,某个线程修改了参数。

此外,对于参数类型可以被不可信任方子类化的参数,不要使用clone方法进行保护性拷贝。

进一步地,为了防止外部程序通过get方法获取对象实例,并通过set方法篡改参数值,最好在get方法中返回对象的保护性拷贝。

最后,如果拷贝的成本收到限制,并且类信任其使用者不会进行非法的参数修改操作,那么就可以在文档中进行说明使用者的职责是不得修改这些收到影响的组件,以此来替代保护性拷贝。

Item 51: Design method signatures carefully

谨慎设计方法签名

谨慎地选择方法的名称

方法的名称应当遵循标准的命名规范:

  • 易于理解
  • 和包内其他的名称风格保持一致
  • 选择与大众认可的名称

不要过于追求提供便利的方法

避免过长的参数列表

最好把参数个数控制在4个以内。

  • 将一个方法分割为多个方法,每个子方法的参数列表是其子集
  • 创建辅助类,保存参数的分组
  • 采用Builder模式,如果方法中带有多个参数,尤其是当他们的参数是可选的时候,最好定义一个对象来表示所有的参数

参数类型优先使用接口,而不是类

boolean参数,优先使用两个元素的枚举类型,这样方便以后的拓展。

Item 52: Use overloading judiciously

慎用重载。

调用哪个重载方法是在编译时做出决定的。对于重载方法的选择是静态的,对于被覆盖的方法的选择,则是动态的。

重载是编译时多态,重写是运行时多态。

如果API的普通用户根本不知道,对于一组给定的参数,其中哪个重载方法将会被调用,使用这样的API就会很容易导致错误。因此,应该避免胡乱使用重载机制

永远不要实现两个具有相同参数数量的重载方法

Item 53: Use varargs judiciously

慎用可变长参数

可变参数方法可以接受0个或者多个指定类型的参数。可变参数的机制为,先创建一个数组,数组的大小为调用位置所传递参数的数量,然后将参数值传递到数组中,最后将数组传递给方法。

在使用可变参数之前,要先包含所有必要的参数,并且要关注可变参数所带来的性能影响。

Item 54: Return empty collections or arrays, not nulls

返回零长度的集合或者数组,而不是null值

若担心分配零长集合或者数组损害了程序的性能,可以通过重复返回同一个不可变的零长集合,避免分配的执行。

Item 55: Return optionals judiciously

谨慎返回optional

Optional<T>类代表的是一个不可变的容器,可以存放的单个非nullT引用,或者什么内容都没有。在某些特殊的情况下,返回optional比抛出异常的方法使用起来更加地灵活,也更加容易,并且比返回null的方法更加不容易出现错误。

  • Optional.empty(): 返回一个空的optional
  • Optional.of(vlaue): 返回指定的非nulloptional

如果方法返回一个optional,该方法不能够返回有效值的时候,可以通过以下方式指定一个默认值:

  • orElse():提供一个默认值,并且是及时加载的
  • orElseThrow()
  • orElseGet():通过调用一个Supplier来获取默认值,但是是懒加载的

Item 56: Write doc comments for all exposed API elements

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

为了正确地编写API文档,必须在每个被导出的类、接口、构造器、方法和属性声明之前增加一个文档注释。

方法的文档注释应该简洁地描述其和客户端之间的约定。

为泛型或者方法编写文档时,确保要在文档中说明所有的参数类型。

为枚举类编写文档时,确保在文档中说明常量,以及类型,还有任何公有的方法。

为注解类型编写文档时,确保在文档中说明所有成员,以及类型本身。