检查参数的有效性
方法在执行前,首先对参数进行检查,如果参数不合法,应该清楚地以一个适当的异常指明错误的原因。对于非公有方法,调用者应该保证传入的参数是合理的,所以应该以assert断言参数的有效性。
但“检查参数的有效性”这条规则也有例外,比如检查工作十分昂贵,或者不切实际,并且在计算过程中有效性检查工作也被隐含完成时。
需要时使用保护性拷贝
public class Period {
private final Date start;
private final Date end;
public Period(Date start, Date end) {
// if (start.compareTo(end) > 0) { //不是线程安全的
// throw new IllegalArgumentException();
// }
//
// this.start = start;
// this.end = end;
this.start = new Date(start.getTime()); // 保护性拷贝是在检查参数有效性之前进行。
this.end = new Date(end.getTime());
if (this.start.compareTo(this.end) > 0) {
throw new IllegalArgumentException();
}
}
public Date start() {
// return this.start;
return (Date) this.start.clone();
}
public Date end() {
// return this.end;
return (Date) this.end.clone();
}
}
Period中被注释掉的代码,没有采用保护性的拷贝。这样的话,Period保存的是两个引用,start和end返回的也是引用,那么用户可以通过引用改变Period的状态,导致start>end非法情况出现。
注意到Period的构造方法中,保护性拷贝是在检查参数有效性之前进行,为什么呢?
这是为了线程安全考虑。假如线程A刚好执行完参数有效性检查,OK了,然后紧接的执行线程B,线程B改变了end的值,使start>end。轮到线程A执行了,有效性检查OK了,开始拷贝..此时start和end的值已经不合法了。
谨慎使用重载方法
java中,重载方法的选择是在编译时决定的,跟类的多态不同。所以有些时候代码行为可能出乎我们的意料。
比如如下代码输出的是三个"unknow",而不是分别输出"set""list""unknow"
public class Test {
public static String classify(Set s) {
return "set";
}
public static String classify(List l) {
return "list";
}
public static String classify(Collection c) {
return "unknow";
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Collection[] c = new Collection[] { new HashSet(), new ArrayList(),
new HashMap().values() };
for (Collection collection : c) {
System.out.println(classify(collection)); // 重载方法的选择是在编译时决定的,因为collection的类型是Collection,所以选择第三个重载方法。
}
}
}
为了使如上代码按照我们所想象的那样工作,应该在Collection的重载方法中,用instanceof判断参数的具体类型再做具体的逻辑。
对于重载方法,最好还是别用了吧...
看看java的ObjectOutputStream类,它的write方法没有采用重载,而是采用了变形方法:writeBoolean(boolean) writeInt(int) writeLong(long)返回0长度的数组而不是null
经常可以看到如下代码:public Cheese[] getCheeses() {
if (cheeseInstock.size() == 0) {
return null;
......
}
}
这样,用户使用这个接口时,还要写额外代码去判断数组是否为null,这很容易出错,建议应该返回0大小的数组而不是null。