本頁說明 Contraband 的類型系統,其基於 GraphQL 類型系統。
Contraband 可用於存取現有的 JSON 基礎 API,或實作您自己的服務。
由於我們不希望依賴特定的程式語言語法來描述 Contraband 綱要,我們將擴充 GraphQL 的綱要語言。
Contraband 綱要應以 *.contra
副檔名儲存。
Contraband 綱要最基本的組件是記錄類型,它僅代表您可以從服務中擷取的一種物件,以及它擁有哪些欄位。在 Contraband 綱要語言中,我們可以這樣表示它
package com.example
@target(Scala)
## Character represents the characters in Star Wars.
type Character {
name: String!
appearsIn: [com.example.Episode]!
}
讓我們來檢視一下,以便我們有一個共同的詞彙
com.example
是此綱要的套件名稱。此套件名稱將用於產生的程式碼。@target(Scala)
是套件的註解。這表示程式碼產生預設將以 Scala 為目標。##
表示記錄類型的文件註解。Character
是一種 Contraband 記錄類型,表示它是一種具有某些欄位的類型。您綱要中的大多數類型都將是記錄類型。在 Java 和 Scala 中,它被編碼為類別。name
和 appearsIn
是 Character
類型上的欄位。這表示 name
和 appearsIn
是唯一可以出現在 Character
類型的 JSON 物件中的欄位。String
是其中一種內建的純量類型。String!
表示該欄位是必要的,這表示服務承諾在您查詢此欄位時,永遠會提供您一個值。在綱要語言中,我們將用驚嘆號來表示這些。[Episode]!
代表 Episode
記錄的列表。由於它也是必要的,因此當您查詢 appearsIn
欄位時,您始終可以預期會收到一個列表(包含零個或多個項目)。現在您知道 Contraband 記錄類型的外觀,以及如何讀取 Contraband 綱要語言的基本知識。
為了啟用綱要演變,Contraband 記錄中的欄位可以宣告新增欄位的版本
package com.example
@target(Scala)
type Greeting {
value: String!
x: Int @since("0.2.0")
}
這表示 value
欄位從一開始 ("0.0.0"
) 就存在,但選用的 x
欄位是從版本 "0.2.0"
開始新增的。Contraband 將產生多個建構函式來維護二進位相容性。
由於 Int
是選用的,因此 None
會用作 x
的預設值。若要提供其他預設值,您可以將其寫成如下
package com.example
@target(Scala)
type Greeting {
value: String!
x: Int = 0 @since("0.2.0")
p: Person = { name: "Foo" } @since("0.2.0")
z: Person = raw"Person(\"Foo\")"
}
請注意,0
會自動以選項包裝。
Contraband 提供一組預設的純量類型
字串
布林值
位元組
字元
整數
長整數
短整數
雙精度浮點數
您也可以使用 Java 和 Scala 類別名稱,例如 java.io.File
。
如果您使用類別名稱 (例如 java.io.File
),您還必須提供應如何序列化和還原序列化類型。
也稱為列舉,列舉類型是一種特殊的純量類型,僅限於一組特定的允許值。這讓您可以
以下是 Contraband 綱要語言中列舉定義的外觀
package com.example
@target(Scala)
## Star Wars trilogy.
enum Episode {
NewHope
Empire
Jedi
}
這表示無論我們在綱要中的何處使用類型 Episode
,我們都期望它正好是 NewHope
、Empire
或 Jedi
其中之一。
記錄類型和列舉是您可以在 Contraband 中定義的唯一類型。但是當您在綱要的其他部分中使用類型時,您可以套用其他類型修飾符,這些修飾符會影響這些值的驗證。讓我們來看一個範例
package com.example
@target(Scala)
## Character represents the characters in Star Wars.
type Character {
name: String!
appearsIn: [com.example.Episode]!
friends: lazy [com.example.Character]
}
在這裡,我們使用 String
類型,並在類型名稱後方新增驚嘆號 !
將其標記為必要。
列表的運作方式類似:我們可以使用類型修飾符將類型標記為列表,這表示此欄位會傳回該類型的列表。在綱要語言中,這以將類型括在方括號 [
和 ]
中表示。
延遲類型會延後欄位的初始化,直到第一次使用它時才會初始化。在綱要語言中,這以關鍵字 lazy
表示。
與許多類型系統一樣,Contraband 支援介面。介面是一種抽象類型,包含類型必須包含才能實作介面的一組特定欄位。
例如,您可以擁有一個介面 Character
,代表星際大戰三部曲中的任何角色
package com.example
@target(Scala)
## Character represents the characters in Star Wars.
interface Character {
name: String!
appearsIn: [com.example.Episode]!
friends: lazy [com.example.Character]
}
這表示任何實作 Character
的類型都需要具有這些確切的欄位。
例如,以下是一些可能實作 Character
的類型
package com.example
@target(Scala)
type Human implements Character {
name: String!
appearsIn: [com.example.Episode]!
friends: lazy [com.example.Character]
starships: [com.example.Starship]
totalCredits: Int
}
type Droid implements Character {
name: String!
appearsIn: [com.example.Episode]!
friends: lazy [com.example.Character]
primaryFunction: String
}
您可以看到這兩種類型都具有 Character
介面中的所有欄位,但也帶入了額外的欄位 (totalCredits
、starships
和 primaryFunction
),這些欄位是該特定角色類型特有的。
除了欄位之外,介面還可以宣告訊息。
package com.example
@target(Scala)
## Starship represents the starships in Star Wars.
interface Starship {
name: String!
length(unit: com.example.LengthUnit): Double
}
這表示任何實作 Starship
的類型都需要同時具有確切的欄位和訊息。
作為將 Scala 或 Java 程式碼注入產生程式碼的退出機制,Contraband 提供了特殊的註解符號。
## Example of an interface
interface IntfExample {
field: Int
#x // Some extra code
#xinterface Interface1
#xinterface Interface2
#xtostring return "custom";
#xcompanion // Some extra companion code
#xcompanioninterface CompanionInterface1
#xcompanioninterface CompanionInterface2
}
#x
將程式碼注入到產生類別的主體中。#xinterface
新增其他父類別。#xtostring
用於提供自訂的 toString
方法。#xcompanion
將程式碼注入到產生類別的伴生物件中。#xcompanioninterface
將其他父類別新增至伴生物件。