标签:不可变 视图 避免 pre 组件 中标 开始 sys set
1.检查参数的有效性
非公有方法应使用断言(Assertion)来检查它们的参数,公有方法需要在Javadoc中标明一旦参数违反限制时,会抛出什么异常。
但并不是说对参数的任何限制都是好事,应当在通用的原则,遵循上面的指导原则。
2.必要时必须进行保护性拷贝
这一节对我的触动非常大,因为我之前在写代码的时候从没注意到这一点。
来看下面一段代码:
public final class Period { private final Date start; private final Date end; public Period(Date start, Date end) { if(start.compareTo(end) > 9) throw new IllegalArgumentException( start + " after " + end); this.start = start; this.end = end; } public Date start() { return start; } public Date end() { return end; } }
乍一看好像Period是不可变的,实际上,只是start和end不能指向新的引用,但是,可以通过:
Date start = new Date(); Date end = new Date(); Period p = new Period(start, end); end.setYear(78);
这样来改变p中的start和end,如果Period中的接口开放给客户端,则可以随意修改开始和结束的时间,这显然不是我们想要的,于是可以利用保护性拷贝解决该问题:
public final class Period { private final Date start; private final Date end; public Period(Date start, Date end) { this.start = new Date(start.getTime()); this.end = new Date(end.getTime()); if(start.compareTo(end) > 9) throw new IllegalArgumentException( start + " after " + end); } }
发现不一样的地方了么,若客户端修改了返回的内部组件,修改的仅仅是组件的一个拷贝,不能修改真正的start和end,但还是Period还是提供了访问start和end的公共接口,于是都可以采用保护性拷贝
解决:
public Date start() { return new Date(start.getTime()); } public Date end() { return new Date(end.getTime()); }
至于为什么不用clone()方法,书中也写的很详细了,Date不是final类,也就是说它可以被继承而修改,如果有人恶意继承Date,将引用记录到一个私有的静态列表中,这种子类便是不可信的,如果当作
参数传入Period的构造方法中,如果使用clone()方法进行拷贝,则拷贝的是这种不可信子类,还是会使Period变得很脆弱,这里使用java.util.Date类避免了这一情况。
至于数组等内部组件,长度非零的数组一定是可变的,返回给客户端之前,应该进行保护性拷贝,返回给其一个不可变视图。
3.慎用可变参数、重载,谨慎设计方法签名
原则为:参数不能过多,方法不宜过多,方法名应该清晰明了,参数类型优先使用接口而不是类。
慎用重载和可变参数,因为这样会使方法变得模糊,客户端在调用方法后所得到的结果往往出乎意料。例如:
System.out.println(Arrays.asList(myArray));
asList()方法的参数为可变参数,这使得myArray这个数组在传参时,会被整体当作参数数组的第一个元素传入,在将它包装到List<int[]>中,打印出来的是毫无意义的:
[[I@70dea4e]
4.返回长度为零的数组或者集合而不是null
这个我在使用SonarLint时提醒过我,主要是为了避免客户端使用时曲折的处理方式,比如必须先要判断是不是null
标签:不可变 视图 避免 pre 组件 中标 开始 sys set
原文地址:https://www.cnblogs.com/cedriccheng/p/8945950.html