仓颉开发之——类和接口(9)

一 概述

  • 接口
  • 属性
  • 子类型关系
  • 类型转换

二 类

2.1 class定义

1
2
3
4
5
6
7
8
9
10
11
12
13
class Rectangle {
let width: Int64
let height: Int64

public init(width: Int64, height: Int64) {
this.width = width
this.height = height
}

public func area() {
width * height
}
}

说明

  • class 类型的定义以关键字 class 开头,后跟 class 的名字
  • class中可以定义一系列的成员变量、成员属性、静态初始化器、构造函数、成员函数和操作符函数

2.1.1 class成员变量

1
2
3
4
5
6
class Rectangle {
let width = 10
static let height = 20
}

let l = Rectangle.height // l = 20

说明:

  • class 成员变量分为实例成员变量和静态成员变量
  • 静态成员变量使用 static 修饰符修饰,必须有初值,只能通过类型名访问
  • 实例成员变量定义时可以不设置初值,也可以设置初值,只能通过对象访问

2.1.2 class 静态初始化器

1
2
3
4
5
6
class Rectangle {
static let degree: Int64
static init() {
degree = 180
}
}

说明:

  • 静态初始化器以关键字组合 static init 开头,后跟无参参数列表和函数体,且不能被访问修饰符修饰
  • 函数体中必须完成对所有未初始化的静态成员变量的初始化,否则编译报错
  • 一个 class 中最多允许定义一个静态初始化器,否则报重定义错误

2.1.3 class 构造函数

class 中也支持定义普通构造函数和主构造函数

1-普通构造函数(以关键字 init 开头)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Rectangle {
let width: Int64
let height: Int64

public init(width: Int64) {
this.width = width
this.height = width
}

public init(width: Int64, height: Int64) { // Ok: overloading with the first init function
this.width = width
this.height = height
}

public init(height: Int64) { // Error, redefinition with the first init function
this.width = height
this.height = height
}
}

2-主构造函数

1
2
3
class Rectangle {
public Rectangle(name: String, let width: Int64, let height: Int64) {}
}

2.1.4 class 终结器

1
2
3
4
5
6
7
8
9
10
11
class C {
var p: CString

init(s: String) {
p = unsafe { LibC.mallocCString(s) }
println(s)
}
~init() {
unsafe { LibC.free(p) }
}
}

说明:

  • 终结器的函数名固定为 ~init
  • 终结器一般被用于释放系统资源

2.1.5 class 成员函数

1
2
3
4
5
6
7
8
9
10
11
12
class Rectangle {
let width: Int64 = 10
let height: Int64 = 20

public func area() {
this.width * this.height
}

public static func typeName(): String {
"Rectangle"
}
}

说明:area 是实例成员函数,typeName 是静态成员函数

2.1.6 class 成员的访问修饰符

1-示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package a
public open class Rectangle {
public var width: Int64
protected var height: Int64
private var area: Int64
public init(width: Int64, height: Int64) {
this.width = width
this.height = height
this.area = this.width * this.height
}
init(width: Int64, height: Int64, multiple: Int64) {
this.width = width
this.height = height
this.area = width * height * multiple
}
}

func samePkgFunc() {
var r = Rectangle(10, 20) // Ok: constructor 'Rectangle' can be accessed here
r.width = 8 // Ok: public 'width' can be accessed here
r.height = 24 // Ok: protected 'height' can be accessed here
r.area = 30 // Error, private 'area' cannot be accessed here
}

2-表格

No 修饰符 说明
1 private class内可见
2 internal 当前包及子包内可见
3 protected 当前模块及当前类的子类可见
4 public 模块内外均可见

2.2 This 类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
open class C1 {
func f(): This { // its type is `() -> C1`
return this
}

func f2() { // its type is `() -> C1`
return this
}

public open func f3(): C1 {
return this
}
}
class C2 <: C1 {
// member function f is inherited from C1, and its type is `() -> C2` now
public override func f3(): This { // ok
return this
}
}

var obj1: C2 = C2()
var obj2: C1 = C2()

var x = obj1.f() // During compilation, the type of x is C2
var y = obj2.f() // During compilation, the type of y is C1

说明:

  • This 类型占位符,代指当前类的类型
  • 它只能被作为实例成员函数的返回类型来使用

2.3 创建对象及调用

1
2
3
4
let r = Rectangle(10, 20) // r.width = 10, r.height = 20
let width = r.width // width = 10
let height = r.height // height = 20
let a = r.area() // a = 200

2.4 class 的继承

1
2
3
4
5
6
7
8
9
10
11
open class A {
let a: Int64 = 10
}

class B <: A { // Ok: 'B' Inheritance 'A'
let b: Int64 = 20
}

class C <: B { // Error, 'B' is not inheritable
let c: Int64 = 30
}

说明:

  • 类继承符号是<:,B继承A表示为class B <: A
  • class 仅支持单继承,因此下面这样一个类继承两个类的代码是不合法的
  • sealed 修饰符只能修饰抽象类,表示被修饰的类定义只能在本定义所在的包内被其他类继承

2.4.1 父类构造函数调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
open class A {
A(let a: Int64) {}
}

class B <: A {
let b: Int64
init(b: Int64) {
super(30)
this.b = b
}

init() {
this(20)
}
}

说明:

  • super(args) 的形式调用父类构造函数
  • this(args) 的形式调用本类其它构造函数。

2.4.2 覆盖和重定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
open class A {
public open func f(): Unit {
println("I am superclass")
}
}

class B <: A {
public override func f(): Unit {
println("I am subclass")
}
}

main() {
let a: A = A()
let b: A = B()
a.f()
b.f()
}

说明:

  • 类覆盖使用关键字override

2.5 类与结构体的区别

  • class 是引用类型,struct 是值类型
  • class 之间可以继承,但 struct 之间不能继承

三 接口

3.1 概念

  • 接口用来定义一个抽象类型,它不包含数据,但可以定义类型的行为
  • 一个类型如果声明实现某接口,并且实现了该接口中所有的成员,就被称为实现了该接口
  • 接口的成员可以包含:成员函数、操作符重载函数、成员属性

3.2 接口定义

1
2
3
interface I { // 'open' modifier is optional.
func f(): Unit
}

说明:

  • 关键字 interface 声明,其后是接口的标识符 I I数字
  • interface 也可以使用 sealed 修饰符表示只能在 interface 定义所在的包内继承、实现或扩展该 interface

3.3 接口继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface Addable {
func add(other: Int64): Int64
}

interface Subtractable {
func sub(other: Int64): Int64
}

class MyInt <: Addable & Subtractable {
var value = 0
public func add(other: Int64): Int64 {
value + other
}
public func sub(other: Int64): Int64 {
value - other
}
}

说明:

  • 接口继承与类继承类似,使用<:
  • 实现多个接口使用 & 分隔多个接口,实现的接口之间没有顺序要求。

3.4 接口实现

1
2
3
4
5
6
7
8
9
10
11
12
open class Base {}
class Sub <: Base {}

interface I {
func f(): Base
}

class C <: I {
public func f(): Sub {
Sub()
}
}

说明:类C实现了接口I的f方法

3.5 Any 类型接口()

1
interface Any {}

说明:

  • Any 类型是一个内置的接口
  • 仓颉中所有接口都默认继承它,所有非接口类型都默认实现它

四 属性

4.1 概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Foo {
private var a = 0

public mut prop b: Int64 {
get() {
println("get")
a
}
set(value) {
println("set")
a = value
}
}
}

main() {
var x = Foo()
let y = x.b + 1 // get
x.b = y // set
}

说明

  • 属性(Properties)提供了一个 getter 和一个可选的 setter 来间接获取和设置值。
  • 只需要对数据操作,对内部的实现无感知,便利地实现访问控制、数据监控、跟踪调试、数据绑定等机制。

4.2 属性定义

1
2
3
4
5
6
7
8
9
class Foo {
public prop a: Int64 {
get() { 0 }
}
public mut prop b: Int64 {
get() { 0 }
set(v) {}
}
}

说明:

  • 声明属性使用关键字prop
  • 无 mut 修饰符的属性,这类属性有且仅有定义 getter
  • 有mut 修饰的属性,这类属性必须分别定义 getter(对应取值)和 setter(对应赋值)的实现

4.3 修饰符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
open class A {
private var valueX = 0
private static var valueY = 0

public open prop x: Int64 {
get() { valueX }
}

public static mut prop y: Int64 {
get() { valueY }
set(v) {
valueY = v
}
}
}
class B <: A {
private var valueX2 = 0
private static var valueY2 = 0

public override prop x: Int64 {
get() { valueX2 }
}

public redef static mut prop y: Int64 {
get() { valueY2 }
set(v) {
valueY2 = v
}
}
}

说明:

  • 成员属性也支持 open、override、redef 修饰
  • mut 修饰符
  • override/redef修饰符

4.4 抽象属性

1
2
3
4
5
6
7
interface I {
prop a: Int64
}

abstract class C {
public prop a: Int64
}

说明:

  • 接口或抽象类中的属性
  • 这些抽象属性没有实现

4.5 属性使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A {
public prop x: Int64 {
get() {
123
}
}
public static prop y: Int64 {
get() {
321
}
}
}

main() {
var a = A()
println(a.x) // 123
println(A.y) // 321
}

说明:

  • 属性分为实例成员属性和静态成员属性
  • 成员属性的使用和成员变量的使用方式一样

五 类型转换

5.1 显示类型转换

1
2
3
4
5
6
7
8
9
10
//数值类型之间的转换
let a: Int8 = 10
let b: Int16 = 20
let r1 = Int16(a)

//Rune 到 UInt32 和整数类型到 Rune 的转换
let x: Rune = 'a'
let y: UInt32 = 65
let r1 = UInt32(x)
let r2 = Rune(y)

5.2 is 和 as 操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
open class Base {
var name: String = "Alice"
}
class Derived <: Base {
var age: UInt8 = 18
}

main() {
let a = 1 is Int64
println("Is the type of 1 'Int64'? ${a}")
let b = 1 is String
println("Is the type of 1 'String'? ${b}")

let b1: Base = Base()
let b2: Base = Derived()
var x = b1 is Base
println("Is the type of b1 'Base'? ${x}")
x = b1 is Derived
println("Is the type of b1 'Derived'? ${x}")
x = b2 is Base
println("Is the type of b2 'Base'? ${x}")
x = b2 is Derived
println("Is the type of b2 'Derived'? ${x}")
}

说明:

  • is用来判断是否是某类型的子类型
  • as强制类型转换

六 思维导图

七 参考

  • 仓颉官方文档—类和接口
  • 仓颉编程语言入门教程