仓颉编程语言青少年基础教程:Interface(接口)
仓颉编程语言青少年基础教程:Interface(接口)
Interface(接口)的定义和使用
接口用来定义一个抽象类型,它不包含数据,但可以定义类型的行为。一个类型如果声明实现某接口,并且实现了该接口中所有的成员,就被称为实现了该接口。
接口的成员可以是抽象的,这意味着接口只规定了这些成员的类型信息(即“做什么”),但不提供具体的实现(即“怎么做”),所有具体实现都由实现类完成。
接口支持继承、多实现、静态成员、泛型成员等高级特性。实现接口的类型自动成为该接口的子类型,可以多态使用。
基本接口定义与实现示例:
// 定义接口:声明一个实例函数
interface Flyable {func fly(): Unit // 抽象方法,无实现
}// 实现接口:Bird类实现Flyable
class Bird <: Flyable {public func fly(): Unit { // 必须实现fly()println("鸟扇动翅膀飞行")}
}// 实现接口:Plane类实现Flyable
class Plane <: Flyable {public func fly(): Unit {println("飞机靠引擎飞行")}
}// 测试:接口作为参数,接收所有实现类
func letItFly(f: Flyable): Unit {f.fly()
}main() {let bird = Bird()let plane = Plane()letItFly(bird) // 输出:鸟扇动翅膀飞行letItFly(plane) // 输出:飞机靠引擎飞行
}
编译运行截图:
定义接口语法
[修饰符] interface接口名 {
// 成员列表(函数、属性、操作符重载)
}
注意
• 关键字:interface 是接口声明的固定起始关键字。
• 修饰符(可选):interface 关键字前的修饰符仅支持 open 或 sealed,默认隐含 open 语义(无需显式声明,因为接口本身就隐含了 open 的语义)。sealed 修饰符(非访问修饰符)示该 class 或 interface 只能在当前包内被继承或实现。
• 接口名:符合仓颉标识符规范(如 Flyable、NamedType)。
• 成员列表:接口的核心,定义抽象行为,默认均为 public(不可显式添加其他访问修饰符,如 private/protected),并且接口成员默认隐式为 open(可被实现 / 继承);成员可以是没有实现体的抽象声明(必须由实现类型提供实现),也可以是带有默认实现的具体实现(实现类型可以选择重写或直接使用)。成员类型支持三类:
• 实例成员函数 / 操作符重载函数
• 静态成员函数 / 操作符重载函数
• 成员属性(仅约定类型和可变性 mut)
接口的实现语法
class/struct/enum 类型名 <: 接口1 & 接口2 & ... {
// 必须实现接口所有未提供默认实现的成员
[override] [public] func 接口函数名(参数列表): 返回类型 {
// 实现(返回类型可为接口指定类型的子类型)
}
[static] [redef] [public] func 接口静态函数名(参数列表): 返回类型 {
// 实现
}
}
其中:
• 使用 <: 符号表示“实现”或“继承自”。实现多个接口时,用 & 连接。
• 实现类型必须为接口中的所有抽象成员(没有默认实现的)提供具体的、public 的实现。
• 如果接口中的成员函数或操作符重载函数的返回值类型是 class 类型,那么允许实现函数的返回类型是其子类型。
接口继承规则
子接口
• 对父接口“已有默认实现”的成员:必须给出新的默认实现(override 可选)。
• 对父接口“无默认实现”的成员:可仅声明,也可给默认实现(override 可选)。
下面给出一个完整的接口示例:
interface Vehicle {func start(): Unitfunc stop(): Unit {println("Vehicle stopped") // 默认实现}
}interface Electric {func charge(): Unit
}// Car 实现 Vehicle 和 Electric 接口
class Car <: Vehicle & Electric {public var speed: Float64 = 0.0public override func start(): Unit {println("Car started")}// 使用默认的stop方法public override func charge(): Unit {println("Car is charging")}
}main() {let myCar = Car()myCar.start() // 输出: Car startedmyCar.stop() // 输出: Vehicle stopped (默认实现)myCar.charge() // 输出: Car is charging// 多态let vehicle: Vehicle = myCarvehicle.start() // 输出: Car started
}
编译运行输出:
Car started
Vehicle stopped
Car is charging
Car started
几点重要示例说明:
1. 静态成员与默认实现
接口的静态成员可强制所有实现类型提供类型相关的行为,支持默认实现。示例:
// 定义带静态成员的接口(带默认实现)
interface NamedType {static func getTypeName(): String { // 静态成员默认实现"未知类型"}
}// 实现接口:无需重写静态成员(直接继承默认实现)
class Cat <: NamedType {}// 实现接口:重写静态成员
class Dog <: NamedType {public static func getTypeName(): String { // 自定义实现"Dog"}
}main() {println(Cat.getTypeName()) // 继承默认实现:输出 未知类型println(Dog.getTypeName()) // 自定义实现:输出 Dogprintln(NamedType.getTypeName()) // 直接访问接口静态成员:输出 未知类型
}
注意:
抽象静态成员:实现类型必须提供实现。不能通过接口名直接调用(InterfaceName.staticMethod() 会报错)。
有默认实现的静态成员:实现类型可以不重写该成员。可以通过接口名和实现类型名调用(InterfaceName.staticMethod() 和 ImplementingClass.staticMethod() 均可)。
2. 接口继承与多接口实现
接口可继承其他接口,实现类需满足所有父接口的规范。示例:
// 父接口1:加法行为
interface Addable {func add(other: Int64): Int64
}// 父接口2:减法行为
interface Subtractable {func sub(other: Int64): Int64
}// 子接口:继承两个父接口,新增乘法行为
interface Calculable <: Addable & Subtractable {func mul(other: Int64): Int64
}// 实现子接口:需同时实现add、sub、mul
class MyNumber <: Calculable {var value: Int64 = 0public func add(other: Int64): Int64 {value + other}public func sub(other: Int64): Int64 {value - other}public func mul(other: Int64): Int64 {value * other}
}main() {let num = MyNumber()num.value = 10println(num.add(5)) // 15println(num.sub(3)) // 7println(num.mul(2)) // 20
}
3.冲突与解决
多接口成员默认实现冲突时,实现类型必须手动实现——必须提供自己的实现。示例:
// 接口1:带默认实现的say()
interface Greeter1 {func say(): String {"你好"}
}// 接口2:带默认实现的say()
interface Greeter2 {func say(): String {"Hello"}
}// 同时实现两个接口:say()默认实现冲突,必须手动实现
class Person <: Greeter1 & Greeter2 {public func say(): String {"既要你好,也要Hello" // 自定义实现解决冲突}
}main() {let p = Person()println(p.say()) // 输出:既要你好,也要Hello
}
4. sealed 修饰符
表示该 class 或 interface 只能在当前包内被继承或实现。
在仓颉语言中,sealed 修饰符属于非访问修饰符(也称为 “功能修饰符”),而非访问修饰符(如 public、private 等)。
示例项目/工程目录结构:
demo9/
└── src
├─A
│ ├── a1.cj
│ └── a2.cj
├─B
│ └── b1.cj
└── main.cj // 入口
A/a1.cj 源码:
package demo9.A// 包A中定义sealed接口
sealed interface Secret {func getSecret(): Unit //String
}
A/a2.cj 源码:
package demo9.A// 同包内可实现
public class ASecret <: Secret { public override func getSecret(): Unit {println("A的秘密") }
}
B/b1.cj 源码:
// 包B中尝试实现(编译报错,故注释掉)
package demo9.B
import demo9.A.*// class BSecret <: Secret { // 错误:sealed接口不允许跨包实现
// public override func getSecret(): Unit {
// println("B的秘密") // return "B的秘密"
// }
// }
入口main.cj源码:
package demo9import demo9.A.ASecret// 主函数
main() {let ok = ASecret()ok.getSecret() // 正常输出: A的秘密
}
顺便提示:
若将此示例中函数的类型Unit改为String,将报错。原因是实现类重写接口方法时,返回类型必须与接口中定义的完全一致。
Unit 与 String 的区别:
• Unit 表示 “无返回值”(类似其他语言的 void),方法中可通过 println 输出内容,但无需 return。
• String 要求方法必须通过 return 语句返回一个字符串,否则会报错。
需要如下修改:
A/a1.cj 修正为:
package demo9.A// 接口方法返回String
sealed interface Secret {func getSecret(): String
}
A/a2.cj 修正为:
// 实现类方法也返回String,与接口匹配
public class ASecret <: Secret { public override func getSecret(): String {return "A的秘密" // 返回String,而非仅打印}
}
main.cj 修正为:
package demo9import demo9.A.ASecretmain() {let ok = ASecret()println(ok.getSecret()) // 输出: A的秘密(打印返回的String)
}
Any 类型
Any 类型是一个内置的接口(所有类型的隐式父接口),仓颉中所有接口都默认继承它,所有非接口类型都默认实现它,因此所有类型都可以作为 Any 类型的子类型使用。这意味着不需要程序员自己写interface Any {}。
目前Any类型,必须先通过模式匹配将其转换为具体类型,然后再打印。
注意,仓颉语言目前不能直接使用println(anyObj) 打印输出,因为对Any类型仓颉语言未提相关机制。例如:
main() {
var any: Any = 1
//pringln(any) //行不通
Any = 2.0
//pringln(any) //行不通
Any = "hello, world!"
//pringln(any) //行不通
}
应写为(推荐方案):
func printAny(value: Any) {match (value) {case i: Int64 => println(i)case f: Float64 => println(f)case s: String => println(s)case b: Bool => println(b)case _ => println("unknown type")}
}main() {var any: Any = 1printAny(any) // 输出: 1any = 2.0printAny(any) // 输出: 2.000000any = "hello, world!"printAny(any) // 输出: hello, world!
}
运行截图:
或写为:
func anyToString(value: Any): String {match (value) {case i: Int64 => return i.toString()case f: Float64 => return f.toString()case s: String => return scase b: Bool => return b.toString()case _ => return "unknown"}
}main() {var any: Any = 1println("Value: ${anyToString(any)}") // 输出: Value: 1any = 2.0println("Value: ${anyToString(any)}") // 输出: Value: 2.000000 any = "hello, world!"println("Value: ${anyToString(any)}") // 输出: Value: hello, world!
}
关于Any类型(Any 接口)还可见https://blog.csdn.net/cnds123/article/details/150399926