仓颉语言中sealed 修饰符介绍
以下是官方文档节选:
抽象类总是可被继承的,故抽象类定义时的 open 修饰符是可选的,也可以使用 sealed 修饰符修饰抽象类,表示该抽象类只能在本包被继承。
抽象类可以使用 sealed 修饰符,表示被修饰的类定义只能在本定义所在的包内被其他类继承。sealed 已经蕴含了 public/open 的语义,因此定义 sealed abstract class 时若提供 public/open 修饰符,编译器将会告警。sealed 的子类可以不是 sealed 类,仍可被 open/sealed 修饰,或不使用任何继承性修饰符。若 sealed 类的子类被 open 修饰,则其子类可在包外被继承。sealed 的子类可以不被 public 修饰。
interface 也可以使用 sealed 修饰符表示只能在 interface 定义所在的包内继承、实现或扩展该 interface。sealed 已经蕴含了 public/open 的语义,因此定义 sealed interface 时若提供 public/open 修饰符,编译器将会告警。继承 sealed 接口的子接口或实现 sealed 接口的抽象类仍可被 sealed 修饰或不使用 sealed 修饰。若 sealed 接口的子接口被 public 修饰,且不被 sealed 修饰,则其子接口可在包外被继承、实现或扩展。继承、实现 sealed 接口的类型可以不被 public 修饰。
官方示例也很简短。
【官方文档详见
不太好明白,现探索实践验证介绍如下。
顺便说明,在仓颉语言中,sealed 修饰符属于非访问修饰符(也称为 “功能修饰符”),而非访问修饰符(如 public、private 等)。
sealed 在抽象类中的使用示例
sealed修饰抽象类的核心是 “限制自身的继承范围在本包内”,但允许子类通过open等修饰符打破这个限制,兼顾了封装性和灵活性。
• 同包内的继承、实现、多级继承和方法重写均合法,程序可正常运行并输出预期结果。
• 跨包的继承 / 实现尝试会在编译阶段被拦截,确保密封类型的实现范围被严格控制在设计的包内,增强代码封装性。
示例项目结构(包结构):
demo/
└── src
├─sealed
│ ├── SamePackageImplementations.cj
│ └── SealedTypes.cj
├─other
│ └── DifferentPackageAttempt.cj
└── main.cj // 入口
1.sealed/SealedTypes.cj内容如下:
package demo.sealed
// 定义sealed抽象类
sealed abstract class SealedClass {
//public init() {} // 空参构造
public func hello(): String {
return "Hello from SealedClass"
}
public func whoAmI(): String
}
// 定义sealed接口
sealed interface SealedInterface {
func doSomething(): Unit
}
2.sealed/SamePackageImplementations.cj 内容如下:
package demo.sealed
// 同包内继承sealed类(合法)
public class SamePackageClass <: SealedClass {
public func whoAmI(): String {
return "I'm SamePackageClass (extends SealedClass)"
}
}
// 同包内实现sealed接口(合法)
public class SamePackageInterfaceImpl <: SealedInterface {
public func doSomething(): Unit {
println("SamePackageInterfaceImpl is doing something")
}
}
// 同包内继承并开放给同包进一步继承
public open class OpenSamePackageClass <: SealedClass {
// 关键:给方法添加 open,允许子类重写
public open func whoAmI(): String {
return "I'm OpenSamePackageClass (extends SealedClass)"
}
}
// 同包内继承开放类(现在合法)
public class AnotherSamePackageClass <: OpenSamePackageClass {
public override func whoAmI(): String { // 此时 override 有效
return "I'm AnotherSamePackageClass (extends OpenSamePackageClass)"
}
}
3.other/DifferentPackageAttempt.cj 内容如下:
package demo.other
import demo.sealed.SealedClass
import demo.sealed.SealedInterface
import demo.sealed.OpenSamePackageClass
// // 尝试在不同包继承sealed类(不合法)
// class DifferentPackageClass <: SealedClass { // 编译错误:无法继承sealed类
// public func whoAmI(): String {
// return "This won't compile"
// }
// }
// // 尝试在不同包实现sealed接口(不合法)
// class DifferentPackageInterfaceImpl <: SealedInterface { // 编译错误:无法实现sealed接口
// public func doSomething(): Unit {
// println("This won't compile either")
// }
// }
// // 尝试在不同包继承sealed类的同包子类(不合法)
// class DifferentPackageExtendsOpen <: OpenSamePackageClass { // 编译错误:即使父类是open的,也不能跨包继承
// public override func whoAmI(): String {
// return "This won't compile too"
// }
// }
4.入口文件 main.cj内容如下:
package demo
import demo.sealed.SamePackageClass
import demo.sealed.SamePackageInterfaceImpl
import demo.sealed.AnotherSamePackageClass
main() {
// 测试同包内的sealed类继承
let obj1 = SamePackageClass()
println(obj1.hello())
println(obj1.whoAmI())
// 测试同包内的sealed接口实现
let obj2 = SamePackageInterfaceImpl()
obj2.doSomething()
// 测试同包内的多级继承
let obj3 = AnotherSamePackageClass()
println(obj3.whoAmI())
}
运行截图:
说明:
1. sealed 修饰符的核心作用
• 限制跨包继承 / 实现:被 sealed 修饰的类或接口,只能在当前包内被继承(类)或实现(接口),跨包尝试会直接编译报错。
示例中,demo.sealed 包的 SealedClass(密封类)和 SealedInterface(密封接口),在 demo.other 包中无法被继承或实现。
2. 同包内的合法操作
• 同包继承密封类:在 demo.sealed 包内,SamePackageClass 和 OpenSamePackageClass 可以直接继承 SealedClass(合法)。
• 同包实现密封接口:SamePackageInterfaceImpl 在 demo.sealed 包内实现 SealedInterface(合法)。
• 同包多级继承:密封类的子类(如 OpenSamePackageClass)若被 open 修饰,允许在同包内被进一步继承(如 AnotherSamePackageClass 继承 OpenSamePackageClass)。
3. 跨包的禁止操作
• 无法在其他包(如 demo.other)继承密封类(SealedClass)或其同包子类(OpenSamePackageClass)。
• 无法在其他包实现密封接口(SealedInterface)。
• 即使密封类的子类被 open 修饰(如 OpenSamePackageClass),跨包继承依然被禁止(sealed 限制具有传递性)。
4.还需要注意:方法重写的严格规则
• open 与 override 配对使用:
父类方法必须用 open 修饰,才能允许子类重写(如 OpenSamePackageClass 中的 open func whoAmI())。
子类重写父类方法时,必须显式添加 override 关键字(如 AnotherSamePackageClass 中的 override func whoAmI())。
• 若父类方法未加 open,即使类被 open 修饰,子类也无法重写该方法(会触发编译错误)。
sealed 在接口中的使用示例
示例项目(包)结构
demo9/
└── src
├─A
│ ├── a1.cj
│ └── a2.cj
├─B
│ └── b1.cj
└── main.cj // 入口
A/a1.cj 源码:
// 包A中定义sealed接口
sealed interface Secret {
func getSecret(): Unit //String
}
A/a2.cj 源码:
// 同包内可实现
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的秘密")
// }
// }
入口main.cj源码:
package demo9
import 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 demo9
import demo9.A.ASecret
main() {
let ok = ASecret()
println(ok.getSecret()) // 输出: A的秘密(打印返回的String)
}