1. 程式人生 > >Effective Java 讀書筆記——38:檢查引數的有效性

Effective Java 讀書筆記——38:檢查引數的有效性

一般在方法執行之前先檢查引數的有效性,如果引數值無效,那麼很快它就會失敗,並且清楚的丟擲合適的異常。

如果這個方法沒有檢查引數的異常,那麼可能在方法處理中出現令人費解的異常。更糟糕的有可能是,方法可以正常返回,但是卻使得某個物件處於被破壞的狀態.

丟擲異常

對於公有方法,可以在Javadoc中的@throw標籤來說明違反異常時所丟擲的異常型別。一旦在文件中說明了異常,那麼強加這些型別的異常檢測就會是比較容易的事情,例子如下:

    // Modular Arithmetic Operations

    /**
     * Returns a BigInteger whose value is {@code (this mod m}).  This method
     * differs from {@code remainder} in that it always returns a
     * <i>non-negative</i> BigInteger.
     *
     * @param  m the modulus.
     * @return {@code this mod m}
     * @throws ArithmeticException {@code m} ≤ 0
     * @see    #remainder
     */
    public BigInteger mod(BigInteger m) {
        if (m.signum <= 0)
            throw new ArithmeticException("BigInteger: modulus not positive");

        BigInteger result = this.remainder(m);
        return (result.signum >= 0 ? result : result.add(m));
    }

上面這個例子是BigInteger中的求模方法,取模的時候需要其大於0,否則丟擲異常。

斷言

另外,對於未被匯出的方法,作為包的建立者,你可以控制這個方法在哪些情況下可以被呼叫,因此你可以,也應該確保只將有效的引數傳遞進去。因此,非公有方法通常應該使用斷言(assert)來檢查它們的引數,如下:

	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;
	}

在生產環境中,一般是不支援assert的,因此這樣可以提高效率,沒有成本開銷。所以,assert只在私有方法中使用,因為私有方法的呼叫者開發者,他和被呼叫者之間是一種弱契約關係,或者說沒有契約關係,其間的約束是依靠開發者自己控制的,開發者應該有充分的理由相信自己傳入的引數是有效的。所以,從某種角度上來說,assert只是起到一個預防開發者自己出錯,或者是程式的無意出錯。

另外

有一些引數暫時沒有直接用到,只是儲存起來供以後使用,這種引數的有效性檢查也是尤其重要:
    static List<Integer> intArrayAsList(final int[] a) {
        if (a == null)
            throw new NullPointerException();

        return new AbstractList<Integer>() {
            public Integer get(int i) {
                return a[i];  // Autoboxing (Item 5)
            }

            @Override public Integer set(int i, Integer val) {
                int oldVal = a[i];
                a[i] = val;     // Auto-unboxing
                return oldVal;  // Autoboxing
            }

            public int size() {
                return a.length;
            }
        };
    }

在上面的例子,傳入的是一個int陣列,返回的是它的list檢視。如果傳入的是null,返回了一個新建的list,然後錯誤的null在後面的程式中可能會非常難以定位。因此,構造器檢查引數的有效性是非常重要的,必須保證構造出來的物件是有效的。

例外

儘管在構造器中檢查引數的有效性非常必要,但是也有例外,可能在有效情況下檢查引數的有效性是及其昂貴的,甚至是不切實際的。比如說,Collections.sort(list)。列表中的所有物件都是可以比較的。在排序過程中會自動檢查這些引數的有效性,而並不是在構造的時候檢查引數的有效性。

總結

並非對引數的任何限制都是好事,一般來說要儘可能的通用, 符合實際的需要。假如方法對它能接受的引數都能完成合理的計算,那麼對於引數的限制其實是越少越好的。因此,鼓勵開發者把限制寫到文件中,並在方法的開頭顯式的檢查引數的有效性。