JML起步---使用JML 改進你的Java程式(2) (轉)

worldblog發表於2007-08-14
JML起步---使用JML 改進你的Java程式(2) (轉)[@more@]

Quantification(譯者注:這裡量詞的意思與邏輯學上的量詞意思相近,而不是普通意義上理解的量詞。):namespace prefix = o ns = "urn:schemas--com::office" />


在上面pop()方法的行為規範中,我們說它的返回值要等於peek()方法的返回值,不過我們並沒有看到關於peek()方法的規範。PriorityQueue中peek()方法的行為規範請看下面的程式碼:

 

3  PriorityQueue peek()方法的行為規範

/*@

  @ public normal_behavior

  @  requires ! isEmpty();

  @  ensures elementsInQueue.has( esult);

  @*/

/*@ pure @*/ peek() throws NoSuchElementException;

JML標記要求只有當佇列中至少含有一個元素的時候,才能peek()方法,同時他還要求方法的返回值必須在elementsInQueue之內,也就是說,這個返回值一定是這個佇列中的一個元素。

 

註釋/*@ pure @*/ 表明peek()方法是一個純方法(pure method),純方法是指沒有副作用的方法。JML中只允許使用純方法進行斷言確認,所以我們把peek()宣告為純方法,這樣我們就可以在pop()方法的後置條件中使用peek()方法。大家肯定想知道,為什麼JML只允許使用純方法進行斷言確認?問題是這樣的,如果JML允許使用非純方法進行斷言確認的話,我們稍不注意就會寫出有副作用的行為規範。比如說可能會有這麼一種情況,開啟了斷言確認以後,我們的程式碼正確無誤,可是如果禁止了斷言確認後,我們的程式碼卻不能執行了,或執行出錯了。這樣當然不行!後面,我們還會進一步討論副作用的問題。

 

JML行為規範可以被子類(含子介面)或者是實現介面的類所繼承,這一點與1.4中斷言有所不同。JML關鍵字 also表示當前定義的行為規範與祖先類或被實現的介面中所定義的行為規範一起作用。因而,在 PriorityQueue介面定義的 peek()方法的行為規範同樣適用於 BinaryHeap類中的 peek()方法。這個就意味著,雖然在 BinaryHeap.peek()的行為規範中沒有明確定義, BinaryHeap.peek()的返回值也必須在 elementsInQueue當中。

 

(譯者注:大頂堆和小頂堆是資料結構裡面的概念,分別表示堆排序方法的不同實現方式。堆排序是一種透過調整二叉樹進行排序的方法。)


上面我們給peek()定義的行為規範明顯缺少了一塊,那就是我們根本沒有要求它返回的那個元素具有最大的優先順序。顯然,JCCC的PriorityQueue介面既可以用於大頂堆,也可以用於小頂堆。大頂堆和小頂堆的表現是有些差別的,在小頂堆中優先順序最高的元素值最小,而大頂堆中優先順序最高的元素值最大。因為PriorityQueue並不知道自己被用來進行大頂堆排序還是小頂堆排序,所以指定返回哪個元素的規範必須在實現PriorityQueue介面的類中進行定義。

 

在JCCC 中,類 BinaryHeap實現了PriorityQueue介面。BinaryHeap允許使用它的客戶程式碼在構造中透過一個引數來指定排序方案,也就是透過引數來指定是透過大頂堆方式排序還是透過小頂堆方式排序。我們使用一個boolean模型變數isMinimumHeap來判斷BinaryHeap的排序方式是大頂堆還是小頂堆。下面的例子是BinaryHeap使用isMinimumHeap給peek()方法定義的行為規範:

 

 

4  BinaryHeap 類中peek()方法的行為規範

/*@

  @ also

  @  public normal_behavior

  @  requires ! isEmpty();

  @  ensures

  @   (isMinimumHeap ==>

  @  (forall Object obj;

  @  elementsInQueue.has(obj);

  @  compareObjects( esult, obj)

  @  <= 0)) &&

  @  ((! isMinimumHeap) ==>

  @  (forall Object obj;

  @  elementsInQueue.has(obj);

  @  compareObjects( esult, obj)

  @  >= 0));

  @*/

public Object peek() throws NoSuchElementException


上面程式碼段4中的後置條件包含兩個部分,分別用於大頂堆和小頂堆的情況。“==>”符號的意思是包含(譯者注:這個包含與邏輯學中包含的意思一致)。x ==> y 當且僅當y為真或x為假時取真值。對於小頂堆排序來說,適用下面所列的程式碼:

(forall Object obj;

  elementsInQueue.has(obj);

  compareObjects( esult, obj) <= 0)

上面的程式碼中forall是一個JML量詞。上面forall當所有的obj滿足elementsInQueue.has(obj)為真且compareObjects( esult, obj)返回一個非正數兩個條件時才為真。換言之,當使用compareObjects()進行比較時,peek()方法的返回值一定小於或等於elementsInQueue中每一個元素的值。其他的JML量詞還有exists、sum以及 min等等。

 

Comparator進行比較


BinaryHeap類允許以兩種方法比較元素的大小。一種方法是要進行比較的元素自己實現Comparable介面,比較過程使用元素中定義的方法進行。另外一種方法是客戶類在構造BinaryHeap類的例項時向建構函式傳入一個Comparator物件,使用該Comparator物件進行比較。無論使用哪一種比較方式,我們都使用模型域comparator來表示比較大小所用的Comparator物件。 在peek()方法的後置條件中,compareObjects()方法使用客戶端選擇的比較方式來進行元素的比較。compareObjects()方法定義如下:

 

5  compareObjects() 方法

/*@

  @ public normal_behavior

  @  ensures

  @  (comparator == null) ==>

  @  ( esult == ((Comparable) a).compareTo(b)) &&

  @  (comparator != null) ==>

  @  ( esult == comparator.compare(a, b));

  @

  @ public pure model int compareObjects(Object a, Object b)

  @ {

  @ if (m_comparator == null)

  @  return ((Comparable) a).compareTo(b);

  @ else

  @  return m_comparator.compare(a, b);

  @ }

  @*/

compareObjects方法的定義中使用了另外一個關鍵字model,它的意思是compareObjects方法是一個模型方法。模型方法是隻能用在行為規範中的JML方法。模型方法定義在的註釋中,所以常規的Java程式碼不能使用。

 

如果BinaryHeap類的客戶程式碼指定了一個特殊的Comparator用來進行比較的話,m_comparator就指向那個Comparator,否則m_comparator的值就是null。compareObjects()方法檢查m_comparator的值,然後採用適當的方法進行元素間的比較。

 


在程式碼段4中我們討論了peek()方法的後置條件。這個條件保證peek()方法的返回值的優先順序大於或者等於模型域elementsInQueue中所有的元素的優先順序。那麼有一個問題,像elementsInQueue這樣的模型域如何取值?前置條件、後置條件和不變數都是沒有副作用的,所以不能使用它們來設定模型域的值。

 

JML使用一個represents語句把模型域與具體的實現域關聯起來。比如下面的represents語句用來給模型域isMinimumHeap賦值:

//@ private represents isMinimumHeap

這個語句的意思是模型域isMinimumHeap的值等於m_isMinHeap的值,其中,m_isMinHeap是BinaryHeap類中一個私有的布林變數。 一旦需要用到isMinimumHeap的值,JML就會把m_isMinHeap的值賦給它。

 

represents語句並沒有限制

 

6  elementsInQueue represents語句

/*@ private represents elementsInQueue

  @ 

  @  Arrays.asList(m_elements)

  @  .subList(1, m_size + 1));

  @*/

從這裡我們可以看出,elementsInQueue的元素就是陣列m_elements[]從第一個元素到第m_size個元素共m_size個元素構成的列表,其中陣列m_elements[]是BinaryHeap的一個私有成員,用來優先順序佇列中的元素,m_size是m_elements[]中正在使用的元素的個數。類BinaryHeap沒有使用m_elements[0],這樣可以簡化對於的操作。JMLObjectBag.convertFrom()的作用是把一個List結構轉化為一個elementsInQueue所需要的JMLObjectBag結構。這樣一旦JML執行時進行斷言檢查的時候需要elementsInQueue的值,就會計算represents 語句

 

下面我們來看一下副作用和異常行為。

 

其它部分請參考:

/develop/read_article.?id=19198">http://www.csdn.net/develop/read_article.asp?id=19198 JML起步---使用JML 改進你的Java(1)
http://www.csdn.net/develop/read_article.asp?id=19200 JML起步---使用JML 改進你的Java程式(3)
http://www.csdn.net/develop/read_article.asp?id=19202 JML起步---使用JML 改進你的Java程式(4)


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-956695/,如需轉載,請註明出處,否則將追究法律責任。

相關文章