2015062705 - EffactiveJava笔记 - 第38条 检查参数的有效性

来源:互联网 发布:mac口红哪款滋润度好 编辑:程序博客网 时间:2024/05/17 20:22

    第七章 方法

    主要涉及方法的以下方面:如何处理参数和返回值,如何设计方法签名,如何为方法编写文档.适合普通方法和构造方法.

   焦点集中在可用性,健壮性和灵活性上.

 

   第38条 检查参数的有效性

   20150627 星期六 北京

   ----->01. 参数检查的必要性

   绝大多数方法和构造方法对于传递给它们的参数都会有某些限制,比如对象引用不能为null,比如必须是正数等.

你应该在文档中(或者注释中)清楚地指出所有这些限制,并且在方法体的开头出检查参数,并且强制施加这些限制.

   这就是发生错误之后尽快检测出错误,这一普遍性原则的具体情形.如果做不到这一点,检测出错误的可能性就很小,即使检测出错误,也难以确定错误根源.

   [类比现实,如果奶粉有问题,如果确定奶粉的原材料没有问题,那么就可以确定加工过程出问题.如果奶粉原材料不能确定有没有问题,那么解决奶粉出问题消耗的时间和精力成本翻倍!]

   (好的做法)如果传递无效的参数值给方法,方法执行前对参数检查,那么方法很快会失败,并且清楚地出现错误的异常.

   (差的做法)如果方法没有检测它的参数,就可能会发生以下问题.其一,不太糟糕的结果,该方法可能在处理过程中失败,并且产生令人费解的异常;其二,比较糟糕的结果,该方法正常返回,但是会瞧瞧计算出错误的结果;其三,最糟糕的结果,该方法可以正常返回,但是使得某个对象处于破坏的状态,将来在某个不确定的情况下,在某个不相关点上引发错误.

   [使用方法参数之前,检查方法参数的有效性,你做了多少呢?很庆幸,我做的还不错!重新温故知新,感触深刻!]

 

   ------>02.共有方法参数检查

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

   常用的异常如下:

IllegalArgumentException      非null的参数不正确

IllegalStateExcetion           对于方法调用而言,对象状态不正确

NullPointException         在禁止使用null的情况下参数值为null

IndexOutOfBoundException    下标参数值越界

ConcurrentModificationException 禁止并发修改情况下,对象进行修改

UnsupportedOperationException   对象不支持用户请求的方法

    一旦在文档中记录对于方法参数的限制,并且记录一旦违反这些限制将抛出异常,强加这些限制就是必须的事情了.例如:

     private BigInteger n;

 

     /**

      * 求模运算

      * @param m the modulus,which must be positive

      * @return this mod m

      * @throwsArithmeticException if m is less then or equal to 0

      */

     public int mod(BigInteger m){

            if (m.signum() <= 0 ) {

                   throw new ArithmeticException("Modulus <=0 :  " + m);

            }

            return this.n.signum() % m.signum();

     }

 

    ------>03.非共有方法参数检查

   对于私有方法而言,你可以控制这个方法在那些情况下被调用,必须确保只将有效的参数传递进来.所以,非公有方法一般使用断言来检查参数的有效性.例如:

     private static void sort(long[] a, int offset, int length) {

            assert a != null;

            assert offset >= 0 && offset <= a.length;

            assert length >= 0 && length <= a.length -offset;

            //do something

     }

   做测试之后,如果断言为假后,程序将不会做任何执行.

   本质上讲,断言是在声称被断言的条件为真,无论外围包的客户端如何使用它,异于有效性检查,断言失败将会抛出AsserttionError.如果断言没有发生作用,本质上不会产生成本开销.

 

    ------>04.保留的方法参数检查

   对于有些参数,方法本身没有用到,保存起来供以后使用.检测这些参数的有效性尤为重要.

例如:

     static List<Integer> intArrayAsList (final int[] a) {

            if (a == null) {

                   throw new NullPointerException();

            }

           

            return new AbstractList<Integer>() {

 

                   @Override

                   public Integer get(int index) {

                          return a[index];

                   }

 

                   @Override

                   public int size() {

                          return a.length;

                   }

            };

     }

    示例中,传入一个int数组,返回List视图.如果这个方法的客户端传递null,该方法会抛出一个NullPointerException,因为该方法显示的条件检查.

   如果忽略这个条件检查,就是返回一个新建的List实例的引用,一旦客户端企图使用这个引用,就会立即抛出空指针异常,那时候想要找出List实例的来源就可能非常困难,使得开发调试工作极大地复杂化.

   有些参数被方法保护起来供以后使用,构造方法代表这种原则的特殊情景,所以检查构造方法参数的有效性是非常重要的,可以避免构造出来的对象违反类的约束条件.

 

   ------>05.例外情况

   在方法执行计算任务之前,应该先检查它的参数,这个规则有例外.一个重要的例外是,有效性检查非常昂贵,或者根本不切实际,或者有效性检查已经隐含在计算过程中完成.例如:考虑为一个对象排序的方法:

Collections.sort(List),列表中的所有对象必须是可以相互比较的.在为列表排序过程中,列表中的每个对象与其他对象进行比较.

   如果这些对象不能进行比较,其中的某个比较操作就会发生异常ClassCastException,这正是sort方法应该做的事情.因此,提前检查列表的元素是否可以进行比较,这并没有多大的意义.

   然而,请注意,不加选择使用这种方法将会导致失去失败原子性(以后异常会说到失败原子性,参考第64条).

 

   有时,某些计算会隐式地执行必要的有效性,如果检查不成功,就会抛出错误的异常,而此异常与文档中表明的方法抛出的异常并不相符.这种情况下,应该使用第61条中讲述的异常转移技术,将计算过程中抛出的异常转换为正确的异常.

   注意不要从本条目中得到如下结论:对于参数的任何限制都是好事,相反,在设计方法时,应该使它们尽可能地通用,并符合实际的需要.假如方法对于它能够接受的所有参数值都能够完成合理的工作,那么参数的限制应该越少越好.然而,通常情况下,有些限制对于被实现的抽象是固有的.

   当编写方法或者构造方法的时候,应该考虑它的参数有哪些限制,应该把这些限制写到文档中,并且在这个方法的开头处,通过显示的检查来实施这些限制.养成这个习惯是非常必要的.

 

 

 

 

 

 

0 0