類別使用一個小型語言定義,類似於模組語言。
類別類型是類別層級的類型表達式對應物:它們指定了類別的一般形狀和類型屬性。
|
表達式 類別類型路徑 等同於綁定到名稱 類別類型路徑 的類別類型。同樣地,表達式 [ 類型表達式1 , … 類型表達式n ] 類別類型路徑 等同於綁定到名稱 類別類型路徑 的參數化類別類型,其中類型參數已分別實例化為 類型表達式1, … 類型表達式n。
類別類型表達式 類型表達式 -> 類別類型 是類別函數的類型(從值到類別的函數),它接受一個類型為 類型表達式 的值作為參數,並返回一個類型為 類別類型 的類別作為結果。
類別類型表達式 object [( 類型表達式 )] { 類別欄位規格 } end 是一個類別主體的類型。它指定了其實例變數和方法。在這個類型中,類型表達式 與自身類型匹配,因此為自身類型提供了一個名稱。
如果類別主體為類別主體類型中指定的所有組件提供定義,並且這些定義滿足類別主體類型中給定的類型要求,則類別主體將與類別主體類型匹配。此外,類別主體中存在的所有虛擬或公開方法也必須存在於類別主體類型中(另一方面,可以省略某些實例變數和具體的私有方法)。虛擬方法將匹配具體方法,這使得忘記其實現成為可能。不可變的實例變數將匹配可變的實例變數。
自 OCaml 4.06 起,類別類型中支援局部開啟。
繼承建構子 inherit 類別主體類型 提供了從其他類別類型包含方法和實例變數的功能。來自 類別主體類型 的實例變數和方法類型被添加到當前類別類型中。
實例變數的規格寫成 val [mutable] [virtual] 實例變數名稱 : 類型表達式,其中 實例變數名稱 是實例變數的名稱,而 類型表達式 是其預期的類型。標誌 mutable 表示這個實例變數是否可以被物理修改。標誌 virtual 表示這個實例變數沒有被初始化。它可以稍後通過繼承初始化。
實例變數規格將隱藏任何先前具有相同名稱的實例變數規格。
方法的規格寫成 method [private] 方法名稱 : 多型類型表達式,其中 方法名稱 是方法的名稱,而 多型類型表達式 是其預期的類型,可能具有多型。標誌 private 表示該方法無法從物件外部存取。
多型可以在公開方法規格中隱式保留:任何未綁定到類別參數且未出現在類別規格其他位置的類型變數都將被假定為通用,並在結果方法類型中設為多型。撰寫明確的多型類型將停用此行為。
如果同一個方法存在多個規格,它們的類型必須相容。方法的任何非私有規格都會強制它成為公開的。
虛擬方法規格寫成 method [private] virtual 方法名稱 : 多型類型表達式,其中 方法名稱 是方法的名稱,而 多型類型表達式 是其預期的類型。
建構子 constraint 類型表達式1 = 類型表達式2 強制兩個類型表達式相等。這通常用於指定類型參數:透過這種方式,它們可以綁定到特定的類型表達式。
類別表達式是類別層級的值表達式對應物:它們評估為類別,因此為類別類型中表達的規格提供實現。
|
|
另請參閱以下語言擴充功能:局部抽象類型、屬性 和 擴充節點。
表達式 class-path 會評估為繫結至名稱 class-path 的類別。同樣地,表達式 [ typexpr1 , … typexprn ] class-path 會評估為繫結至名稱 class-path 的參數化類別,其中類型參數已分別實例化為 typexpr1, …typexprn。
表達式 ( class-expr ) 會評估為與 class-expr 相同的模組。
表達式 ( class-expr : class-type ) 會檢查 class-type 是否符合 class-expr 的類型(也就是說,實作 class-expr 是否符合類型規格 class-type)。整個表達式會評估為與 class-expr 相同的類別,但 class-type 中未指定的所有組件都會被隱藏,且無法再存取。
類別應用以(可能標記的)表達式的並列表示。它表示其建構子是應用於給定引數的第一個表達式的類別。引數的評估方式與表達式應用相同,但建構子本身只會在建立物件時評估。特別是,建構子應用所造成的副作用只會在物件建立時發生。
表達式 fun [[?]label-name:]pattern -> class-expr 會評估為從值到類別的函式。當此函式應用於值 v 時,此值會與模式 pattern 比對,而結果是在擴充環境中評估 class-expr 的結果。
從具有預設值的函式轉換為僅具有模式的函式,其運作方式與一般函式的類別函式完全相同。
表達式
是以下項目的簡短形式
如同核心語言表達式一樣,let 和 let rec 建構會在本機繫結值名稱。
如果局部定義發生在類別定義的最開始處,則它會在建立類別時評估(就像定義在類別外部一樣)。否則,它會在呼叫物件建構子時評估。
自 OCaml 4.06 以來,類別表達式中支援局部開啟。
|
表達式 object class-body end 表示類別主體。這是物件的原型:它會列出此類別的物件的實例變數和方法。
類別主體是類別值:它不會立即評估。相反地,它的組件會在每次建立物件時評估。
在類別主體中,模式 ( pattern [: typexpr] ) 會與 self 比對,因此提供 self 和 self 類型的繫結。Self 只能在方法和初始化子中使用。
Self 類型不能是封閉的物件類型,這樣類別才能保持可延伸性。
自 OCaml 4.01 以來,如果在同一個類別主體中多次定義相同的方法或實例變數名稱,則會發生錯誤。
繼承建構 inherit class-expr 允許重複使用其他類別的方法和實例變數。類別表達式 class-expr 必須評估為類別主體。此類別主體的實例變數、方法和初始化子會新增到目前的類別中。新增方法會覆寫任何先前定義的同名方法。
祖先可以透過在繼承建構中附加 as lowercase-ident 來繫結。lowercase-ident 不是真正的變數,只能用於選取方法,也就是說,在表達式 lowercase-ident # method-name 中。這可讓您存取方法 method-name(如同它在父類別中定義的一樣),即使它在目前的類別中重新定義。此祖先繫結的範圍僅限於目前的類別。祖先方法可以從子類別呼叫,但只能間接呼叫。
定義 val [mutable] inst-var-name = expr 會新增實例變數 inst-var-name,其初始值是表達式 expr 的值。旗標 mutable 允許方法實際修改此變數。
實例變數只能在其定義之後的方法和初始化子中使用。
自 3.10 版本起,使用相同名稱重新定義的可見實例變數不會建立新的變數,而是會合併,並使用最後一個值進行初始化。它們必須具有相同的型別和可變性。但是,如果一個實例變數因為在介面中省略而隱藏,它將會與其他具有相同名稱的實例變數保持區隔。
變數規格寫作 val [mutable] virtual inst-var-name : typexpr。它指定了變數是否可修改,並給出其型別。
虛擬實例變數是在 3.10 版本中新增的。
方法定義寫作 method method-name = expr。方法的定義會覆寫此方法先前的任何定義。如果任何定義聲明為公開的,則該方法將會是公開的(即非私有的)。
私有方法,method private method-name = expr,是一種只能在 self 上調用的方法(來自相同物件的其他方法,定義在此類別或其子類別中)。此調用使用表達式 value-name # method-name 執行,其中 value-name 在類別定義的開頭直接綁定到 self。私有方法不會出現在物件型別中。一個方法可以同時具有公開和私有的定義,但是一旦有一個公開的定義,所有後續的定義都將被設為公開。
方法可以具有顯式多型型別,允許它們在程式中以多型方式使用(即使對於同一個物件也是如此)。顯式宣告可以透過以下三種方式之一完成:(1)在方法定義中,緊接在方法名稱之後,給出一個顯式多型型別,即 method [private] method-name : { ' ident }+ . typexpr = expr;(2)透過虛擬方法定義預先宣告顯式多型型別;(3)透過繼承和/或約束 self 的型別來匯入此宣告。
在方法主體中,有一些特殊的表達式可用於操作實例變數和複製 self
|
表達式 inst-var-name <- expr 會就地修改目前的物件,方法是將與 inst-var-name 關聯的值替換為 expr 的值。當然,此實例變數必須已宣告為可變的。
表達式 {< inst-var-name1 = expr1 ; … ; inst-var-namen = exprn >} 會評估為目前物件的複本,其中實例變數 inst-var-name1, …, inst-var-namen 的值已替換為對應的表達式 expr1, …, exprn 的值。
方法規格寫作 method [private] virtual method-name : poly-typexpr。它指定了該方法是公開還是私有,並給出其型別。如果該方法旨在成為多型,則該型別必須是顯式多型。
自 Ocaml 3.12 起,關鍵字 inherit!、val! 和 method! 具有與 inherit、val 和 method 相同的語義,但是它們還額外要求它們引入的定義必須是覆寫。也就是說,method! 要求 method-name 已經在此類別中定義,val! 要求 inst-var-name 已經在此類別中定義,而 inherit! 要求 class-expr 覆寫某些定義。如果沒有發生此類覆寫,則會發出錯誤信號。
作為副作用,這 3 個關鍵字會避免警告 7(方法覆寫)和 13(實例變數覆寫)。請注意,警告 7 預設是停用的。
建構 constraint typexpr1 = typexpr2 強制兩個型別表達式相等。這通常用於指定型別參數:透過這種方式,它們可以綁定到特定的型別表達式。
類別初始化器 initializer expr 指定了一個表達式,該表達式將在從該類別建立物件時(一旦所有實例變數都已初始化)評估。
|
類別定義 class class-binding { and class-binding } 是遞迴的。每個 class-binding 都定義一個 class-name,該名稱可以在整個表達式中使用,但繼承除外。它也可以用於繼承,但只能在其自身之後的定義中使用。
類別綁定將類別名稱 class-name 綁定到表達式 class-expr 的值。它還將類別型別 class-name 綁定到類別的型別,並定義了兩個型別縮寫:class-name 和 # class-name。第一個是此類別物件的型別,而第二個更為通用,因為它會與屬於子類別的任何物件的型別統一(請參閱第 11.4 節)。
如果類別的某個方法是虛擬的(即,出現在類別型別中,但實際上沒有定義),則必須將該類別標記為虛擬。無法從虛擬類別建立物件。
類別型別參數對應於類別型別和類別綁定定義的兩個型別縮寫的型別參數。它們必須使用型別約束綁定到類別定義中的實際型別。為了使縮寫形式良好,類別的推斷型別的型別變數必須是型別參數或在約束子句中綁定。
|
這是簽名中類別定義的對應項。如果類別規格和類別定義具有相同的型別參數且其型別匹配,則它們是匹配的。
|
類別型別定義 class class-name = class-body-type 定義了類別主體型別 class-body-type 的縮寫 class-name。如同類別定義,也定義了兩個型別縮寫 class-name 和 # class-name。此定義可以透過一些型別參數進行參數化。如果類別型別主體中的任何方法是虛擬的 (virtual),則必須標記定義為 virtual。
如果兩個類別型別定義具有相同的型別參數,並且展開後會匹配的型別,則它們會匹配。