在仓颉编程语言中,泛型指的是参数化类型,参数化类型是一个在声明时未知并且需要在使用时指定的类型。function、class、interface、struct 与 enum 的声明都可以是泛型的。
泛型中的几个常用术语:
类型形参:一个类型或者函数声明可能有一个或者多个需要在使用处被指定的类型,这些类型就被称为类型形参。
类型变元:在声明类型形参后,当通过标识符来引用这些类型时,这些标识符被称为类型变元。
类型实参:当在使用泛型声明的类型或函数时指定了泛型参数,这些参数被称为类型实参。
类型构造器:一个需要零个、一个或者多个类型作为实参的类型称为类型构造器。
interface List<T>{ //T 声明时 是类型形参, List 类型构造器
func put(item:T):Unit //T 引用时 是类型变元
func get(index:Int64):T //T类型变元
}
class ArrayList <:List<String>{ //指定了泛型参数 String 类型实参
public func put(item:String){}
public func get(index:Int64):String{
return 'item'
}
}
泛型函数
如果一个函数声明了一个或多个类型形参,则将其称为泛型函数。语法上,类型形参紧跟在函数名后,并用 <> 括起,如果有多个类型形参,则用 , 分离。
func f1<T>(a: T): T { //全局泛型函数
return a
}
func f2(a: Int64):(Int64) -> Int64 {
func f<T>(a: T): T { a } //局部泛型函数
func double(b: Int64): Int64 { a+ b }
return f<Int64>~>double
}
class A {
//interface、class、struct、enum 的成员函数可以是泛型的
func foo<T>(a: T): Unit where T <: ToString {
println("${a}")
}
//interface、class、struct、enum 与 extend 中可以定义静态泛型函数
public static func fromArray<T>(l: ArrayList<T>): (T, T) {
return (l[0], l[1])
}
}
泛型接口
public interface Iterable<E> {
func iterator(): Iterator<E>
}
public interface Iterator<E> <: Iterable<E> {
func next(): Option<E>
}
泛型类
泛型类的静态成员变量或属性的类型声明和表达式中不能引用类型参数或包含未实例化泛型类型表达式。另外,静态变量或属性初始化表达式中不能调用泛型类的静态成员函数或属性。
class A<T> {}
class B<T> {
static func foo() {1}
static var err1: A<T> = A<T>() // Error, 静态成员不能依赖于泛型参数
static prop err2: A<T> { // Error, 静态成员不能依赖于泛型参数
get() {
A<T>() // Error,静态成员不能依赖于泛型参数
}
}
static var vfoo = foo() // Error, 等同于 'static var vfoo = B<T>.foo()'
static var ok: Int64 = 1
}
main() {
B<Int32>.ok = 2
println(B<Int64>.ok) // 2
}
泛型结构体
struct Pair<T, U> {
let x: T
let y: U
public init(a: T, b: U) {
x = a
y = b
}
public func first(): T {
return x
}
public func second(): U {
return y
}
}
main() {
var a: Pair<String, Int64> = Pair<String, Int64>("hello", 0)
println(a.first())
println(a.second())
}
泛型枚举
在仓颉编程语言的泛型 enum 类型设计中,Option 类型是一个典型的示例。Option<T> 分成两种情况,一种是 Some(T),用来表示一个有值的返回结果,另一种是 None 用来表示一个空的结果。
public enum Option<T> {
Some(T)
| None
}
类型别名
类型别名的定义以关键字 type 开头,接着是类型的别名,然后是等号 =,最后是原类型。只能在源文件顶层定义类型别名,并且原类型必须在别名定义处可见。
type int = Int64 // 给数据类型定义一个别名
class HarmonyOS_NEXT{
HarmonyOS_NEXT(public var name:String){}
}
type HO = HarmonyOS_NEXT // 给类定义一个别名
type op<T> = Option<T> // 泛型别名
main() {
var i:int = 100
var ho = HO('鸿蒙')
var op = op<Int64>.Some(100)
println(ho.name) //鸿蒙
println(i) //100
println(op) //Some(100)
}
泛型类型的子类型关系
先拿大家熟悉的Java泛型举个例子:
协变:用 ? extends 父类,如 List<? extends Animal> 可以接收 List<Cat>
逆变:用 ? super 子类,如 List<? super Cat> 可以接收 List<Animal>
在仓颉语言中,用户定义的类型构造器在其类型参数处是不型变的。
内建类型的型变规则
元组类型:对每个元素类型是协变的(例如 (D, D) 是 (C, C) 的子类型,因为 D <: C)
函数类型:入参类型逆变,返回值类型协变。
open class Animal {}
class Cat <: Animal {}
// 函数类型:接收 Animal
func feedAnimal(animal: Animal) {}
// 函数类型:接收 Cat
func feedCat(cat: Cat) {}
func getCat():Cat{
return Cat()
}
main() {
// 允许将 feedAnimal 赋值给接收 Cat 的函数变量(入参逆变)
let f: (Cat) -> Unit = feedAnimal; // 合法:因为 Animal 是 Cat 的父类(C=Cat <: A=Animal)
let f2:()->Animal;
f2 = getCat // 返回协变
println("Hello World")
}
泛型约束
语法为在函数、类型的声明体之前使用 where 关键字来声明,对于声明的泛型形参 T1, T2,可以使用 where T1 <: Interface, T2 <: Class 这样的方式来声明泛型约束,同一个类型变元的多个约束可以使用 & 连接。例如:where T1 <: Interface1 & Interface2。
abstract class Ball {
public func play(): String
}
class Football <: Ball {
public func play(): String {
return "踢足球"
}
}
class Basketball <: Ball {
public func play(): String {
return "打篮球"
}
}
class Sport<T> where T <: Ball { //类型约束 T必须是Ball的子类
var balls: ArrayList<Ball> = ArrayList<Ball>()
public func addBall(a: T) {
balls.add(a)
}
public func plays() {
for(a in balls) {
println(a.play())
}
}
}
class Cake{
public func eat(){}
}
main() {
var sport: Sport<Ball> = Sport<Ball>()
sport.addBall(Football())
sport.addBall(Basketball())
// sport.addBall(Cake()) //类型不匹配 不能添加
sport.plays() //踢足球 打篮球
}