本页介绍了基于 GraphQL 类型系统的走私类型系统。
走私可以用来访问现有的基于 JSON 的 API,或者实现你自己的服务。
由于我们不想依赖于特定的编程语言语法,为了讨论走私模式,我们将扩展 GraphQL 的模式语言。
走私模式应该以文件扩展名 *.contra
保存。
走私模式的最基本组成部分是记录类型,它只代表你可以从你的服务中获取的一种对象类型,以及它包含哪些字段。在走私模式语言中,我们可以这样表示它
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
是一个走私记录类型,这意味着它是一种带有某些字段的类型。你模式中的大多数类型将是记录类型。在 Java 和 Scala 中,它被编码为一个类。name
和 appearsIn
是 Character
类型上的字段。这意味着 name
和 appearsIn
是 Character
类型中唯一可以出现在 JSON 对象中的字段。String
是内置标量类型之一。String!
表示该字段是必需的,这意味着服务承诺在查询此字段时始终为你提供一个值。在模式语言中,我们将用感叹号表示它们。[Episode]!
表示一个 Episode
记录列表。由于它也是必需的,因此你始终可以在查询 appearsIn
字段时期望得到一个列表(包含零个或多个项目)。现在你知道了走私记录类型是什么样的,以及如何阅读走私模式语言的基础知识。
为了支持模式演变,走私记录中的字段可以声明添加它的版本
package com.example
@target(Scala)
type Greeting {
value: String!
x: Int @since("0.2.0")
}
这意味着 value
字段从一开始就存在 ("0.0.0"
),但可选的 x
字段是从版本 "0.2.0"
开始添加的。走私将生成多个构造函数以维护二进制兼容性。
由于 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
将自动包装为选项。
走私自带了一组默认的标量类型
String
Boolean
Byte
Char
Int
Long
Short
Double
你也可以使用 Java 和 Scala 类名,例如 java.io.File
。
如果你使用 java.io.File
等类名,你还需要提供类型如何序列化和反序列化的信息。
枚举类型(也称为枚举)是一种特殊的标量类型,它被限制为一组特定的允许值。这可以让你
以下是枚举定义在走私模式语言中的样子
package com.example
@target(Scala)
## Star Wars trilogy.
enum Episode {
NewHope
Empire
Jedi
}
这意味着,无论我们在模式中使用 Episode
类型,我们都期望它完全是 NewHope
、Empire
或 Jedi
中的一个。
记录类型和枚举是你在走私中可以定义的唯一类型。但是,当你在模式的其他部分使用类型时,你可以应用额外的类型修饰符,这些修饰符会影响这些值的验证。让我们看一个例子
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
表示。
与许多类型系统一样,走私支持接口。接口是一个抽象类型,它包含一个类型必须包含的特定字段集,才能实现该接口。
例如,你可以有一个接口 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 代码注入到生成的代码中的一个安全机制,走私提供了特殊的注释标记。
## 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
向伴随对象添加额外的父类。