`
xusulong
  • 浏览: 79889 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Effective Java 2 读书笔记 第7章 方法

阅读更多

第38条:检查参数的有效性

当编写方法或者构造器的时候,应该考虑它的参数有哪些限制。应该把这些限制写到文档中,并且在每个方法的开头处,通过显示的检查来实施这些限制。即应该在发生错误之后尽快检测出错误 ,可以避免很多异常和不正确的却无法检测的结果。
通过进行有效性检查,付出的努力远远小于带来的异常。

例外 :有效检查工作非常昂贵。

但并不是意味着,对参数的任何限制都是好事,相反,在设计方法时候,应该使得他们尽可能通用,并符合实际的需要。然而在通常情况下,有的限制对被实现的抽象来说是固有的。

 

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

  1. 设计安全的语言,需要加设类的客户端会尽其所能来破坏这个类的约束条件,因此你必须保护性地设计程序
  2. 对于每个构造器的每个可变参数进行保护性拷贝是必要的,注意,这个操作是在检查参数的有效性(见38)之前,并且有效性检查是针对拷贝之后的对象,不是针对原始的对象
  3. 对于参数类型可以被不可信任方子类化的参数,不要使用clone方法进行保护性拷贝
  4. 包括对传入的构造器的参数进行保护性拷贝,在内部组件返回给客户端,也需要保护性拷贝

总之:如果类具有从客户端得到或者返回到客户端的可变组件,类就必须保护性地拷贝这些组件。如果拷贝的成本收到限制,并且类信任它的客户端不会不恰当地修改足球,就可以在文档中指名客户端的职责是不得修改受影响的组件,以此来代替保护性拷贝

 

第40条:谨慎设计方法签名

  1. 谨慎选择方法的名称,始终遵循标准的命名习惯,与大众认可相一致
  2. 不要过于追求提供便利的方法。每个方法应该尽其所能,类中太多的方法难以学习,使用,文档化等,只有一个操作被使用的很频繁的时候,考虑为她提供快捷方式,否则不提供
  3. 避免过长的参数列表,小于等于4个。三种方法解决次问题,
    1. 分解方法成多个
    2. 用辅助类保存参数
    3. 从对象构架到方法调用采用Builder模式
  4. 参数类型,优先接口而不是类,boolean,优先使用两个元素的枚举类型(不是很明白

第41条:慎重重载

看下面的程序,

// Broken! - What does this program print?
public class CollectionClassifier {
public static String classify(Set<?> s) {
return "Set";
}
public static String classify(List<?> lst) {
return "List";
}
public static String classify(Collection<?> c) {
return "Unknown Collection";
}
public static void main(String[] args) {
Collection<?>[] collections = {
new HashSet<String>(),
new ArrayList<BigInteger>(),
new HashMap<String, String>().values()
};
for (Collection<?> c : collections)
System.out.println(classify(c));
}
}
 

我们期望的是打印Set,List, Unknown Collection,然后确实三次“Unknown Collection”

因为对于重载方法(overloaded method)的选择是静态(编译时)的,而对于覆盖(overriden method)的方法的选择是动态(运行时)的

 

  1. 应该避免胡乱地使用重载机制
  2. 安全而保守的策略,用于不要导出两个具有相同参数数目的重载方法,可以用不同的名称而不是重载。如果采用可变参数,则不要重载它
  3. 对于多个具有相同参数数目的方法来说,应该尽量避免重载,构造器往往不可避免,在此情况,要避免 这样的情形,同一组的参数只需要经过类型转换就可以被传递给不同的重载方法。如果不能避免,则要保证所有重载方法的行为一直。否则,程序员就很难有效利用,不理解为什么不能正常工作

第42条:慎用可变参数

可变参数方法可以接受0个或者多个指定类型的参数

// Simple use of varargs
static int sum(int... args) {
int sum = 0;
for (int arg : args)
sum += arg;
return sum;
}

 有时候会有这样的需求,需要一个或者多个某种类型参数的方法,这时候需要指定一个正常参数和一个可变参数比较好

// The WRONG way to use varargs to pass one or more arguments!
static int min(int... args) {
if (args.length == 0)
throw new IllegalArgumentException("Too few arguments");
int min = args[0];
for (int i = 1; i < args.length; i++)
if (args[i] < min)
min = args[i];
return min;
}

 因为可变参数的这个特性,我们可以讲数组当作final参数的现有方法,改造成可变参数代替,而不影响现有的客户端,然后可以并不意味着应该这么做,Arrays.asList就会出现不恰当的使用

对于:

public static void main(String[] args) {
int[] digits = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 4 };
System.out.println(Arrays.asList(digits));
}

 Arrays.asList将digits的对象引用包装到了List<int[]>中,而不是我们想要的那种每一个元素都包装,要想产生这种结果,可以这样:

// The right way to print an array
System.out.println(Arrays.toString(myArray));

 因此 不必改造具有final数组参数的每个方法,只有当确实是在数量不顶的值上执行调用时使用可变参数

 

对于重视性能的程序来说,使用可变参数更要小心,如果某个方法的95%调用会有≤3个参数,则重载5个,超过了的用可变参数,如

public void foo() { }
public void foo(int a1) { }
public void foo(int a1, int a2) { }
public void foo(int a1, int a2, int a3) { }
public void foo(int a1, int a2, int a3, int... rest) { }

 在定义参数数目不定的方法时,可变参数是一种很方便的方式,但是不能被滥用

 

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

返回null值会要求客户端中必须有额外的代码来处理null返回值,例如:

private final List<Cheese> cheesesInStock = ...;
/**
* @return an array containing all of the cheeses in the shop,
* or null if no cheeses are available for purchase.
*/
public Cheese[] getCheeses() {
if (cheesesInStock.size() == 0)
return null;
...
}
 

客户端需要:

Cheese[] cheeses = shop.getCheeses();
if (cheeses != null &&
Arrays.asList(cheeses).contains(Cheese.STILTON))
System.out.println("Jolly good, just the thing.");
}
 

而不是:

if (Arrays.asList(shop.getCheeses()).contains(Cheese.STILTON))
System.out.println("Jolly good, just the thing.");

 返回数组也不必担心性能,因为0长度数组是不可变的,可以共享,在类中可以有一个static 的0长度数组来提供使用

如:

// The right way to return a copy of a collection
public List<Cheese> getCheeseList() {
if (cheesesInStock.isEmpty())
return Collections.emptyList(); // Always returns same list
else
return new ArrayList<Cheese>(cheesesInStock);
}

 

第44条:为所有到处的API元素编写文档注释

如果想要一个API真正可用,就必须为其编写文档。javadoc利用特殊格式的稳定注释(documentation comment),根据源代码自动产生API文档。

详细的规范可以参考:Sun的 How to Write Doc Comments

 

为了正确地编写API文档,必须在每个被到处的类,接口,构造器,方法和域声明之前增加一个文档注释。如果类是序列化的,也应该对它的序列化形式编写文档。

 

注意事项:

  1. @param、@return、@throws标签后面的短语或者子句都不用句点来结束。
  2. 使用html标签会被转换成HTML
  3. 使用代码片段放在{@code}中
  4. 特殊字符文档,比如小于号,放在{@literal}中
  5. 文档第一句话成注释所属元素的概要描述,因此要注意句点的使用
  6. 方法和构造器,概要最好是完整的动词短语,而类,接口和域,应该是名词短语
  7. 关于泛型,枚举和注解(后两者体验不深
    1. 为泛型或者方法编写文档,确保说明所以的类型参数
    2. 枚举,说明常量
    3. 注解,确保说明所有成员已经类型本身

简而言之:要为API编写文档,文档注释是最好的最有效的途径。

 

0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics