綱要與類型 

本頁說明 Contraband 的類型系統,其基於 GraphQL 類型系統。

Contraband 可用於存取現有的 JSON 基礎 API,或實作您自己的服務。

Contraband 綱要語言 

由於我們不希望依賴特定的程式語言語法來描述 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]!
}

讓我們來檢視一下,以便我們有一個共同的詞彙

現在您知道 Contraband 記錄類型的外觀,以及如何讀取 Contraband 綱要語言的基本知識。

since 註解 

為了啟用綱要演變,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),您還必須提供應如何序列化和還原序列化類型。

列舉類型 

也稱為列舉,列舉類型是一種特殊的純量類型,僅限於一組特定的允許值。這讓您可以

  1. 驗證此類型的任何引數是否為允許值之一。
  2. 透過類型系統傳達欄位永遠會是有限的一組值之一。

以下是 Contraband 綱要語言中列舉定義的外觀

package com.example
@target(Scala)

## Star Wars trilogy.
enum Episode {
  NewHope
  Empire
  Jedi
}

這表示無論我們在綱要中的何處使用類型 Episode,我們都期望它正好是 NewHopeEmpireJedi 其中之一。

必要類型 

記錄類型和列舉是您可以在 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 介面中的所有欄位,但也帶入了額外的欄位 (totalCreditsstarshipsprimaryFunction),這些欄位是該特定角色類型特有的。

訊息 

除了欄位之外,介面還可以宣告訊息。

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
}