Generic Constraints
Inspect one language lane at a time so line-level text and code deltas stay readable.
Diff Lane
English
0 modified sections0 code block delta0 anchor delta
Diff Lane
中文
1 modified sections0 code block delta0 anchor delta
modified泛型约束text+1 line
v1.0.5
Section Text
1
泛型约束的作用是在 function、class、interface、struct、enum 声明时明确泛型形参所具备的操作与能力。只有声明了这些约束才能调用相应的成员函数。在很多场景下泛型形参是需要加以约束的,以 `id` 函数为例:2
3
<!-- compile -->4
5
6
开发者唯一能做的事情就是将函数形参 `a` 这个值返回,而不能进行 `a + 1`,`println("${a}")` 等操作,因为它可能是一个任意的类型,比如 `(Bool) -> Bool`,这样就无法与整数相加,同样因为是函数类型,也不能通过 `println` 函数来输出在命令行上。而如果这一泛型形参上有了约束,那么就可以做更多操作了。7
8
约束大致分为接口约束与 class 类型约束。在函数或类型的声明体之前,可以使用 `where` 关键字来声明泛型约束。对于声明的泛型形参 `T1, T2`,可以使用 `where T1 <: Interface, T2 <: Class` 这样的方式来声明泛型约束。如果同一个类型变元的多个约束,可以使用 `&` 连接,例如,`where T1 <: Interface1 & Interface2`。9
10
仓颉中的 `println` 函数能接受类型为字符串的参数。如果需要把一个泛型类型的变量转为字符串后打印在命令行上,可以对这个泛型类型变元加以约束,这个约束是 `core` 中定义的 `ToString` 接口,显然它是一个接口约束:11
12
13
这样就可以利用这个约束,定义一个名为 `genericPrint` 的函数:14
15
<!-- verify -->16
17
18
结果为:19
20
21
如果 genericPrint 函数的类型实参没有实现 ToString 接口,那么编译器会报错。例如传入一个函数做为参数时:22
23
<!-- compile.error -->24
25
26
如果对上面的文件进行编译,那么编译器会抛出泛型类型参数不满足约束的错误。因为 `genericPrint` 函数的泛型的类型实参不满足约束 `(Int64) -> Int64 <: ToString`。27
28
除了上述通过接口来表示约束,还可以使用 class 类型来约束一个泛型类型变元。例如:当要声明一个动物园类型 `Zoo<T>`,但是需要这里声明的类型形参 `T` 受到约束,这个约束就是 `T` 需要是动物类型 `Animal` 的子类型, `Animal` 类型中声明了 `run` 成员函数。这里声明两个子类型 `Dog` 与 `Fox` 都实现了 `run` 成员函数,这样在 `Zoo<T>` 的类型中,就可以对于 `animals` 数组列表中存放的动物实例调用 `run` 成员函数:29
30
<!-- verify -->31
32
33
程序的输出为:34
35
36
> **注意:**37
>38
> 泛型变元的约束只能是具体的 class 类型或 interface,且变元如果存在多个 class 类型的上界时,它们必须在同一继承链路上。Code 1 · cangjie
1
func id<T>(a: T) {2
return a3
}Code 2 · cangjie
1
package std.core // `ToString` is defined in core.2
3
public interface ToString {4
func toString(): String5
}Code 3 · cangjie
1
func genericPrint<T>(a: T) where T <: ToString {2
println(a)3
}4
5
main() {6
genericPrint<Int64>(10)7
}Code 4 · text
1
10Code 5 · cangjie
1
func genericPrint<T>(a: T) where T <: ToString {2
println(a)3
}4
5
main() {6
genericPrint<(Int64) -> Int64>({ i => 0 })7
}Code 6 · cangjie
1
import std.collection.ArrayList2
3
abstract class Animal {4
public func run(): String5
}6
7
class Dog <: Animal {8
public func run(): String {9
return "dog run"10
}11
}12
13
class Fox <: Animal {14
public func run(): String {15
return "fox run"16
}17
}18
19
class Zoo<T> where T <: Animal {20
var animals: ArrayList<Animal> = ArrayList<Animal>()21
public func addAnimal(a: T) {22
animals.add(a)23
}24
25
public func allAnimalRuns() {26
for(a in animals) {27
println(a.run())28
}29
}30
}31
32
main() {33
var zoo: Zoo<Animal> = Zoo<Animal>()34
zoo.addAnimal(Dog())35
zoo.addAnimal(Fox())36
zoo.allAnimalRuns()37
}Code 7 · text
1
dog run2
fox runv1.1.0
Section Text
1
泛型约束的作用是在 function、class、interface、struct、enum 声明时明确泛型形参所具备的操作与能力。只有声明了这些约束才能调用相应的成员函数。在很多场景下泛型形参是需要加以约束的,以 `id` 函数为例:2
3
<!-- compile -->4
5
6
开发者唯一能做的事情就是将函数形参 `a` 这个值返回,而不能进行 `a + 1`,`println("${a}")` 等操作,因为它可能是一个任意的类型,比如 `(Bool) -> Bool`,这样就无法与整数相加,同样因为是函数类型,也不能通过 `println` 函数来输出在命令行上。而如果这一泛型形参上有了约束,那么就可以做更多操作了。7
8
约束大致分为接口约束与 class 类型约束。在函数或类型的声明体之前,可以使用 `where` 关键字来声明泛型约束。对于声明的泛型形参 `T1, T2`,可以使用 `where T1 <: Interface, T2 <: Class` 这样的方式来声明泛型约束。如果同一个类型变元的多个约束,可以使用 `&` 连接,例如,`where T1 <: Interface1 & Interface2`。9
10
仓颉中的 `println` 函数能接受类型为字符串的参数。如果需要把一个泛型类型的变量转为字符串后打印在命令行上,可以对这个泛型类型变元加以约束,这个约束是 `core` 中定义的 `ToString` 接口,显然它是一个接口约束:11
12
<!-- code_no_check -->13
14
15
这样就可以利用这个约束,定义一个名为 `genericPrint` 的函数:16
17
<!-- verify -->18
19
20
结果为:21
22
23
如果 genericPrint 函数的类型实参没有实现 ToString 接口,那么编译器会报错。例如传入一个函数做为参数时:24
25
<!-- compile.error -->26
27
28
如果对上面的文件进行编译,那么编译器会抛出泛型类型参数不满足约束的错误。因为 `genericPrint` 函数的泛型的类型实参不满足约束 `(Int64) -> Int64 <: ToString`。29
30
除了上述通过接口来表示约束,还可以使用 class 类型来约束一个泛型类型变元。例如:当要声明一个动物园类型 `Zoo<T>`,但是需要这里声明的类型形参 `T` 受到约束,这个约束就是 `T` 需要是动物类型 `Animal` 的子类型, `Animal` 类型中声明了 `run` 成员函数。这里声明两个子类型 `Dog` 与 `Fox` 都实现了 `run` 成员函数,这样在 `Zoo<T>` 的类型中,就可以对于 `animals` 数组列表中存放的动物实例调用 `run` 成员函数:31
32
<!-- verify -->33
34
35
程序的输出为:36
37
38
> **注意:**39
>40
> 泛型变元的约束只能是具体的 class 类型或 interface,且变元如果存在多个 class 类型的上界时,它们必须在同一继承链路上。Code 1 · cangjie
1
func id<T>(a: T) {2
return a3
}Code 2 · cangjie
1
package std.core // `ToString` is defined in core.2
3
public interface ToString {4
func toString(): String5
}Code 3 · cangjie
1
func genericPrint<T>(a: T) where T <: ToString {2
println(a)3
}4
5
main() {6
genericPrint<Int64>(10)7
}Code 4 · text
1
10Code 5 · cangjie
1
func genericPrint<T>(a: T) where T <: ToString {2
println(a)3
}4
5
main() {6
genericPrint<(Int64) -> Int64>({ i => 0 })7
}Code 6 · cangjie
1
import std.collection.ArrayList2
3
abstract class Animal {4
public func run(): String5
}6
7
class Dog <: Animal {8
public func run(): String {9
return "dog run"10
}11
}12
13
class Fox <: Animal {14
public func run(): String {15
return "fox run"16
}17
}18
19
class Zoo<T> where T <: Animal {20
var animals: ArrayList<Animal> = ArrayList<Animal>()21
public func addAnimal(a: T) {22
animals.add(a)23
}24
25
public func allAnimalRuns() {26
for(a in animals) {27
println(a.run())28
}29
}30
}31
32
main() {33
var zoo: Zoo<Animal> = Zoo<Animal>()34
zoo.addAnimal(Dog())35
zoo.addAnimal(Fox())36
zoo.allAnimalRuns()37
}Code 7 · text
1
dog run2
fox run