当前位置: 首页 > news >正文

仓颉编程语言青少年基础教程:class(类)(下)

仓颉编程语言青少年基础教程:class(类)(下)

本文接着上一篇博文,继续讲解仓颉编程语言class(类)。

继承(Inheritance)

仓颉只支持单继承。用 <: 指定父类,父类必须用 open(非抽象类需用 open 修饰) 或 abstract 修饰才能被继承。

父类必须用 open 或 abstract 修饰才能被继承:

  • 非抽象类 —— 必须加 open 才能被继承;什么都不写就是 internal 且 封闭。

  • 抽象类 —— 默认即“可继承”,写不写 open 都行;若想再限制只能本包继承,就加 sealed。

示例 1:普通类加上 open,继承才成功

// 1. 普通类,显式 open,才允许继承open class Base {public func hello() { println("Base") }
}// 2. 继承
class Child <: Base {      public func hi() { println("Child") }
}main(): Int64 {let c = Child()c.hello()   // Basec.hi()      // Childreturn 0
}

编译运行截图:

示例 2 :抽象类无需 open,直接可继承

// 1. 抽象类,默认就是“可继承”
abstract class AbstractBase {public func core(): Unitpublic func helper() { println("helper") }
}// 2. 非抽象子类直接继承 —— 合法
class Impl <: AbstractBase {public override func core() { println("Impl::core") }
}main(): Int64 {let obj: AbstractBase = Impl()obj.core()    // Impl::coreobj.helper()  // helperreturn 0
}

抽象类使用 sealed 修饰符

抽象类可以使用 sealed 修饰符,表示被修饰的类定义只能在本定义所在的包内被其他类继承。sealed 已经蕴含了 public/open 的语义,因此定义 sealed abstract class 时若提供 public/open 修饰符,编译器将会告警。sealed 的子类可以不是 sealed 类,仍可被 open/sealed 修饰,或不使用任何继承性修饰符。若 sealed 类的子类被 open 修饰,则其子类可在包外被继承。sealed 的子类可以不被 public 修饰。

sealed修饰抽象类的核心是 “限制自身的继承范围在本包内”,但允许子类通过open等修饰符打破这个限制,兼顾了封装性和灵活性。

【在仓颉语言中,sealed 修饰符属于非访问修饰符(也称为 “功能修饰符”),而非访问修饰符(如 public、private 等)。
sealed修饰抽象类的核心是 “限制自身的继承范围在本包内”,但允许子类通过open等修饰符打破这个限制,兼顾了封装性和灵活性。
•  同包内的继承、实现、多级继承和方法重写均合法,程序可正常运行并输出预期结果。
•  跨包的继承 / 实现尝试会在编译阶段被拦截,确保密封类型的实现范围被严格控制在设计的包内,增强代码封装性。】

sealed 仅允许同包内的类型实现,先看一个简单示例:

示例项目结构:
demo12/
└── src
├── A
│   ├── base.cj        // 定义 sealed 基类
│   └── derived_ok.cj  // 同包继承,应成功
├── B
│   └── derived_bad.cj // 跨包继承,应报错
└── main.cj            // 入口

1. A/base.cj 内容如下:

package demo12.A// sealed 基类
sealed abstract class Base {  //  不需要public ?public func greet(): Unit
}

2.A/derived_ok.cj 内容如下:

package demo12.Apublic class DerivedOk <: Base {  // 必须加 public// 显式空参构造public init() {}public override func greet(): Unit {println("Hello from DerivedOk")}
}

3.derived_bad.cj内容如下:

package demo12.Bimport demo12.A.Base// 会编译失败: error: ……'
// class DerivedBad <: Base {
//     public override func greet(): Unit {
//         println("Hello from DerivedBad")
//     }
// }

4.入口文件

package demo12//import demo12.A.Base  // ?
import demo12.A.DerivedOk// 主函数
main() {let ok = DerivedOk()ok.greet()      // 正常输出: Hello from DerivedOk
}

再看一个全面点的sealed作用示例:

示例项目结构:
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 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 demoimport demo.sealed.SamePackageClass 
import demo.sealed.SamePackageInterfaceImpl 
import demo.sealed.AnotherSamePackageClassmain() {// 测试同包内的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 修饰,子类也无法重写该方法(会触发编译错误)。

方法覆盖(Override)和重定义(redef

法覆盖:子类用 override 重写父类 open 方法——父类用open修饰的实例函数,子类可通过override重写实现,调用时根据对象运行时类型决定执行哪个版本(动态派发);

静态方法重定义:子类用 redef 重写父类静态方法(按类类型调用)。

示例 1:实例方法覆盖(动态派发)

// 父类 Vehicle
open class Vehicle {public open func run() {  // 需用 open 允许覆盖println("车辆行驶")}
}// 子类 Car
class Car <: Vehicle {public override func run() {  // 覆盖父类方法println("汽车飞驰")}
}main() {let v: Vehicle = Car()  // 编译时类型为 Vehicle,运行时为 Carv.run()  // 输出:汽车飞驰(动态派发,调用子类实现)
}

示例 2:静态方法重定义(按类调用)

// 父类 Tool
open class Tool {static func name(): String {return "工具"}
}// 子类 Hammer
class Hammer <: Tool {redef static func name(): String {  // 重定义静态方法return "锤子"}
}main() {println(Tool.name())   // 输出:工具(父类方法)println(Hammer.name()) // 输出:锤子(子类重定义方法)
}

说明,子类 Hammer使用 redef 关键字重定义了父类 Tool 的静态方法 name(),返回字符串 "锤子"。

属性 (Properties)

属性(Properties)是对 “数据访问” 的封装,通过getter(取值逻辑)和setter(赋值逻辑)间接操作底层数据,隐藏实现细节,支持访问控制、数据验证等功能。使用时与普通变量无异,但内部通过函数实现逻辑。

属性不是变量,而是 “取值函数(getter)+ 可选赋值函数(setter)” 的组合:

  •  getter:无参数,返回属性类型的值(当访问属性时执行)。

  •  setter:接收一个参数(赋值时的输入值),无返回值(当给属性赋值时执行)。

属性的分类

类型

特点

语法关键字

只读属性

仅有 getter,不可赋值

prop

可读写属性

有 getter 和 setter,可赋值

mut prop

抽象属性

无实现(仅声明),需子类 / 实现类实现

抽象类 / 接口中用prop

属性的定义语法

[访问修饰符] [open/override/redef] [mut] prop 属性名: 类型 {
// getter:取值逻辑(必须实现)
get() {
取值表达式; // 返回属性类型的值
}

    // setter:赋值逻辑(仅mut prop需实现)
set(参数名) {
赋值逻辑; // 通常操作底层私有变量
}
}

注意:

①命名形参约束

如果父类/接口里的属性带 mut,子类在 override/redef 时:

  • 必须保留 mut

  • 必须保持类型完全一致

  • setter 的形参名可以不同,但仓颉推荐保持一致以增强可读性。

②属性不能“递归”访问自身

在 get()/set(v) 里直接写 属性名 会无限递归,应改为读写背后的私有字段。

③计算属性 ≠ 缓存属性

getter 每次访问都会重新执行函数体,若计算成本高又想缓存,需手动在类里加私有变量保存结果。

使用属性语法

①读取 (调用 getter)

let x = object.propertyName

②写入 (调用 setter,仅 mut prop 可用)

object.propertyName = newValue

示例1:

class User {// 底层私有变量:存储实际数据(外部不可直接访问)private let _name: String;private var _age: Int64 = 0;// 1. 只读属性:姓名(仅允许读取,不允许修改)public prop name: String {get() {_name; // 直接返回底层变量}}// 2. 可读写属性:年龄(赋值时验证合法性,取值时打印日志)public mut prop age: Int64 {get() {println("[Get] 用户${_name}的年龄:${_age}");_age;}set(newAge) {println("[Set] 尝试设置${_name}的年龄为:${newAge}");// 验证逻辑:年龄必须在0-150之间if (newAge < 0 || newAge > 150) {println("  错误:年龄不合法,设为默认值0");_age = 0;} else {_age = newAge;println("  成功:年龄更新为${_age}");}}}// 构造函数:初始化姓名public init(name: String) {this._name = name;}
}// 程序入口:main函数
main(): Int64 {// 1. 创建User对象let user = User("张三");// 2. 访问只读属性name(不可赋值)println("用户名:${user.name}"); // 输出:用户名:张三// user.name = "李四"; // 错误:只读属性不可赋值// 3. 操作可读写属性age(带验证)user.age = 25; // 合法年龄,成功更新user.age; // 读取年龄,触发getter日志user.age = 200; // 不合法年龄,设为默认值0user.age; // 读取更新后的默认年龄return 0;
}

编译运行结果:

用户名:张三
[Set] 尝试设置张三的年龄为:25 
成功:年龄更新为25
[Get] 用户张三的年龄:25       
[Set] 尝试设置张三的年龄为:200
错误:年龄不合法,设为默认值0
[Get] 用户张三的年龄:0  

示例2:

class Temperature {// 内部的私有成员变量,存储摄氏温度private var _celsius: Float64 = 0.0// 1. 公开的 Celsius 属性,可读写 (mut)public mut prop Celsius: Float64 {get() {_celsius // 直接返回内部存储的值}set(value) {// 可以在 setter 中加入逻辑,比如限制范围_celsius = value}}// 2. 公开的 Fahrenheit 属性,只读 (无 mut)// 它是一个计算属性,值由 Celsius 转换而来,并不直接存储public prop Fahrenheit: Float64 {get() {(Celsius * 9.0 / 5.0) + 32.0}// 无 setter,所以是只读的}// 3. 公开的 Kelvin 属性,可读写// 它的 getter 和 setter 都涉及计算public mut prop Kelvin: Float64 {get() {Celsius + 273.15}set(value) {Celsius = value - 273.15}}
}main() {let temp = Temperature()// 设置 Celsius,会调用它的 settertemp.Celsius = 25.0println("Celsius: ${temp.Celsius}") // 输出: Celsius: 25.000000// 读取 Fahrenheit,会调用它的 getter 进行计算println("Fahrenheit: ${temp.Fahrenheit}") // 输出: Fahrenheit: 77.000000// 读取 Kelvin,会调用它的 getter 进行计算println("Kelvin: ${temp.Kelvin}") // 输出: Kelvin: 298.150000// 通过设置 Kelvin 来改变温度temp.Kelvin = 300.0println("\nAfter setting Kelvin to 300.0:")println("Celsius: ${temp.Celsius}") // 输出: Celsius: 26.850000println("Fahrenheit: ${temp.Fahrenheit}") // 输出: Fahrenheit: 80.330000println("Kelvin: ${temp.Kelvin}") // 输出: Kelvin: 300.000000// 尝试设置只读属性 Fahrenheit 会编译报错// temp.Fahrenheit = 100.0 // Error: Cannot assign to value: 'Fahrenheit' is a get-only property
}

编译运行结果:

Celsius: 25.000000
Fahrenheit: 77.000000
Kelvin: 298.150000   

After setting Kelvin to 300.0:
Celsius: 26.850000
Fahrenheit: 80.330000
Kelvin: 300.000000


文章转载自:

http://VxmLth8U.xtqLd.cn
http://c4F6BaaW.xtqLd.cn
http://omquOX5O.xtqLd.cn
http://udM97HF5.xtqLd.cn
http://2EMhpxPF.xtqLd.cn
http://xqMuprOg.xtqLd.cn
http://hSLni7f6.xtqLd.cn
http://keoAzVNL.xtqLd.cn
http://Rqz5KBZz.xtqLd.cn
http://DXYZzhwM.xtqLd.cn
http://6ezI1v0j.xtqLd.cn
http://7J1C2uqu.xtqLd.cn
http://tklMRi9R.xtqLd.cn
http://fE5ytdEL.xtqLd.cn
http://5QzNDRaY.xtqLd.cn
http://PaoJkE0Q.xtqLd.cn
http://evFbWKWO.xtqLd.cn
http://Dh8fBL81.xtqLd.cn
http://OojojkUF.xtqLd.cn
http://pQIwxz0S.xtqLd.cn
http://cO4TbGjd.xtqLd.cn
http://RL8lGRlZ.xtqLd.cn
http://DGkMw9Ei.xtqLd.cn
http://xSfRvuSK.xtqLd.cn
http://PHhkVO6S.xtqLd.cn
http://oBhQBVjT.xtqLd.cn
http://u3oPrCfj.xtqLd.cn
http://c4gMqoo4.xtqLd.cn
http://FbqzCw2m.xtqLd.cn
http://7ozkYHwG.xtqLd.cn
http://www.dtcms.com/a/387844.html

相关文章:

  • 【LVS入门宝典】深入解析负载均衡:LVS的核心作用与实现原理
  • 7.4缓存
  • vscode单击暂时预览文件 双击持续打开文件
  • 机器视觉 真实项目案例征集
  • 一根网线搞定远程运维,GL-RM1PE 深度体验:远程运维、装机、开机一体化的 KVM over IP
  • Ubuntu20.04仿真 | 云台相机可直接使用文件
  • Docker学习记录——构建本地镜像,从Windows向Ubuntu推送
  • MyBatis分页:PageHelper
  • 基于python BERT的新闻文本分类与用户行为预测的分析与应用
  • MFC实战:OBJ模型加载与3D渲染指南
  • FAQ:珠海网络推广哪家好?GEO优化能带来哪些优势?
  • (论文速读)CLR-GAN: 通过一致的潜在表征和重建提高gan的稳定性和质量
  • MFC_Static
  • TDengine IDMP 基本功能——数据可视化(3. 饼图)
  • Spring基础创建
  • 智能的非数学本质
  • CNB迁移和UI定制
  • 基于OpenTelemetry与Jaeger的分布式追踪原理深度解析与实践指南
  • EasyDSS视频直播RTMP推流技术如何实现多机型的无人机视频统一直播
  • 智能扫地机器人方案开发,基于32位MCU
  • 【STM32 CubeMX + Keil】DAC 输出0~3.3V间任意电压
  • git submodule命令详解
  • HTTP/2.0是什么?
  • 深度学习基础:从线性回归到 Softmax 回归的完整梳理
  • 深度学习之线性回归与 Softmax 回归
  • 线性回归与 Softmax 回归
  • 源雀 Scrm开源:企微防截屏
  • [APItest-Karate] HttpRequestBuilder | HttpClient发送请求
  • 线性回归与 Softmax 回归:从基础模型到深度学习入门
  • 【Leetcode hot 100】105.从前序与中序遍历序列构造二叉树