量詞(Quantification)(譯者注:這裏量詞的意思與邏輯學上的量詞意思相近,而不是普通意義上理解的量詞。)<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
代碼段3 PriorityQueue 中peek()方法的行爲規範
/*@ @ public normal_behavior @ requires ! isEmpty(); @ ensures elementsInQueue.has(/result); @*/ /*@ pure @*/ Object peek() throws NoSuchElementException; |
JML標記要求只有當隊列中至少含有一個元素的時候,才能調用peek()方法,同時他還要求方法的返回值必須在elementsInQueue之內,也就是說,這個返回值一定是這個隊列中的一個元素。
註釋/*@ pure @*/ 表明peek()方法是一個純方法(pure method),純方法是指沒有副作用的方法。JML中只允許使用純方法進行斷言確認,所以我們把peek()聲明爲純方法,這樣我們就可以在pop()方法的後置條件中使用peek()方法。大家肯定想知道,爲什麼JML只允許使用純方法進行斷言確認?問題是這樣的,如果JML允許使用非純方法進行斷言確認的話,我們稍不注意就會寫出有副作用的行爲規範。比如說可能會有這麼一種情況,開啓了斷言確認以後,我們的代碼正確無誤,可是如果禁止了斷言確認後,我們的代碼卻不能運行了,或運行出錯了。這樣當然不行!後面,我們還會進一步討論副作用的問題。
JML行爲規範可以被子類(含子接口)或者是實現接口的類所繼承,這一點與J2SE1.4中斷言有所不同。JML關鍵字 also表示當前定義的行爲規範與祖先類或被實現的接口中所定義的行爲規範一起作用。因而,在 PriorityQueue接口定義的 peek()方法的行爲規範同樣適用於 BinaryHeap類中的 peek()方法。這個就意味着,雖然在 BinaryHeap.peek()的行爲規範中沒有明確定義, BinaryHeap.peek()的返回值也必須在 elementsInQueue當中。
|
大頂堆和小頂堆(譯者注:大頂堆和小頂堆是數據結構裏面的概念,分別表示堆排序方法的不同實現方式。堆排序是一種通過調整二叉樹進行排序的方法。)
在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(/result, obj) @ <= 0)) && @ ((! isMinimumHeap) ==> @ (/forall Object obj; @ elementsInQueue.has(obj); @ compareObjects(/result, obj) @ >= 0)); @*/ public Object peek() throws NoSuchElementException |
上面代碼段4中的後置條件包含兩個部分,分別用於大頂堆和小頂堆的情況。“==>”符號的意思是包含(譯者注:這個包含與邏輯學中包含的意思一致)。x ==> y 當且僅當y爲真或x爲假時取真值。對於小頂堆排序來說,適用下面所列的代碼:
(/forall Object obj; elementsInQueue.has(obj); compareObjects(/result, obj) <= 0) |
上面的代碼中/forall是一個JML量詞。上面/forall表達式當所有的對象obj滿足elementsInQueue.has(obj)爲真且compareObjects(/result, obj)返回一個非正數兩個條件時才爲真。換言之,當使用compareObjects()進行比較時,peek()方法的返回值一定小於或等於elementsInQueue中每一個元素的值。其他的JML量詞還有/exists、/sum以及 /min等等。
使用Comparator進行比較
代碼段5 compareObjects() 方法
/*@ @ public normal_behavior @ ensures @ (comparator == null) ==> @ (/result == ((Comparable) a).compareTo(b)) && @ (comparator != null) ==> @ (/result == 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的註釋中,所以常規的Java代碼不能使用。
如果BinaryHeap類的客戶代碼指定了一個特殊的Comparator用來進行比較的話,m_comparator就指向那個Comparator,否則m_comparator的值就是null。compareObjects()方法檢查m_comparator的值,然後採用適當的方法進行元素間的比較。
JML使用一個represents語句把模型域與具體的實現域關聯起來。比如下面的represents語句用來給模型域isMinimumHeap賦值:
//@ private represents isMinimumHeap <- m_isMinHeap; |
這個語句的意思是模型域isMinimumHeap的值等於m_isMinHeap的值,其中,m_isMinHeap是BinaryHeap類中一個私有的布爾變量。 一旦需要用到isMinimumHeap的值,JML就會把m_isMinHeap的值賦給它。
represents語句並沒有限制<-右邊必須是成員變量。比如說,下面是elementsInQueue的represents語句:
代碼段6 elementsInQueue 的represents語句
/*@ private represents elementsInQueue @ <- JMLObjectBag.convertFrom( @ 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 語句<-符號右邊的代碼並進行求值。
下面我們來看一下副作用和異常行爲。
其它部分請參考:
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)