1 概述
1.1 案例介绍
仓颉编程语言作为一款面向全场景应用开发的现代编程语言,通过现代语言特性的集成、全方位的编译优化和运行时实现、以及开箱即用的 IDE工具链支持,为开发者打造友好开发体验和卓越程序性能。
本案例将介绍仓颉中的反射和注解,并结合简单的编程代码进行知识体验,仓颉开发环境我们使用了开发者空间提供的云主机环境,环境已经预装了仓颉工具链和CodeArts IDE for Cangjie,即开即用,非常便捷。
案例结合代码体验,让大家更直观的了解仓颉语言中的反射和注解。
1.2 适用对象
- 个人开发者
- 高校学生
1.3 案例时间
本案例总时长预计40分钟。
1.4 案例流程

说明:
- 进入华为开发者空间,登录云主机;
- 使用CodeArts IDE for Cangjie编程和运行仓颉代码。
1.5 资源总览
|
资源名称 |
规格 |
单价(元) |
时长(分钟) |
|
开发者空间 - 云主机 |
鲲鹏通用计算增强型 kc2 | 4vCPUs | 8G | Ubuntu |
免费 |
40 |
最新案例动态,请查阅 《仓颉之反射和注解的神秘力量》。小伙伴快来领取华为开发者空间进行实操体验吧!
2 运行测试环境准备
2.1 开发者空间配置
面向广大开发者群体,华为开发者空间提供一个随时访问的“开发桌面云主机”、丰富的“预配置工具集合”和灵活使用的“场景化资源池”,开发者开箱即用,快速体验华为根技术和资源。
领取云主机后可以直接进入 华为开发者空间工作台界面,点击打开云主机 \> 进入桌面连接云主机。没有领取在开发者空间根据指引领取配置云主机即可,云主机配置参考1.5资源总览。


2.2 创建仓颉程序
点击桌面CodeArts IDE for Cangjie,打开编辑器,点击新建工程,保持默认配置,点击创建。
产物类型说明:
- executable,可执行文件;
- static,静态库,是一组预先编译好的目标文件的集合;
- dynamic,动态库,是一种在程序运行时才被加载到内存中的库文件,多个程序共享一个动态库副本,而不是像静态库那样每个程序都包含一份完整的副本。

2.3 运行仓颉工程
创建完成后,打开src/main.cj,参考下面代码简单修改后,点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。
(\* 注意:后续替换main.cj文件代码时,package demo保留)
(\* 仓颉注释语法:// 符号之后写单行注释,也可以在一对 /\* 和 \*/ 符号之间写多行注释)

到这里,我们第一个仓颉程序就运行成功啦!后面案例中的示例代码都可以放到main.cj文件中进行执行,接下来我们继续探索仓颉语言。
3 反射和注解
3.1 动态特性
仓颉的动态特性主要包含反射、动态加载。
3.1.1 仓颉反射基本介绍
反射指程序可以访问、检测和修改它本身状态或行为的一种机制。
反射这一动态特性有以下的优点:
- 提高了程序的灵活性和扩展性。
- 程序能够在运行时获悉各种对象的类型,对其成员进行枚举、调用等操作。
- 允许在运行时创建新类型,无需提前硬编码。
但使用反射调用,其性能通常低于直接调用,因此反射机制主要应用于对灵活性和拓展性要求很高的系统框架上。
3.1.2 如何获得TypeInfo
什么是TypeInfo?
TypeInfo 是一个类型,这个核心类型中记录任意类型的类型信息,并且定义了方法用于获取类型信息、设置值等。
可以使用三种静态的 of 方法来生成 TypeInfo 信息类。
例如可以用反射来获取一个自定义类型的类型信息,具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。

此外,为配合动态加载使用,TypeInfo 还提供了静态函数 get,该接口可通过传入的类型名称获取 TypeInfo。
具体代码操作如下:
注意:传入的类型名称和您的工程名称是对应的。
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。

采用get方法无法获取一个未实例化的泛型类型。
注意:传入的类型名称和您的工程名称是对应的。
具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。

3.1.3 如何使用反射访问成员
在获取到对应的类型信息类即 TypeInfo 后,便可以通过其相应接口访问对应类的实例成员以及静态成员。
注意:仓颉的反射被设计为只能访问到类型内 public 的成员。
例如在运行时对类的某一实例成员变量进行获取与修改。
具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。

通过反射对属性进行检查以及修改。
具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。

通过反射机制进行函数调用,具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。

3.2 注解
仓颉中提供了一些属性宏用来支持一些特殊情况的处理。
3.2.1 正确使用整数运算溢出的注解
仓颉中提供三种属性宏来控制整数溢出的处理策略,即 @OverflowThrowing,@OverflowWrapping 和 @OverflowSaturating ,这些属性宏当前只能标记于函数声明之上,作用于函数内的整数运算和整型转换。
- 抛出异常(throwing):当整数运算溢出时,抛出异常。
具体代码如下:
- 高位截断(wrapping):当整数运算的结果超出用于接收它的内存空间所能表示的数据范围时,则截断超出该内存空间的部分。
具体代码如下:
- 饱和(saturating):当整数运算溢出时,选择对应固定精度的极值作为结果。
具体代码如下:
3.2.2 性能优化注解
为了提升与 C语言互操作的性能,仓颉提供属性宏 @FastNative 控制 cjnative 后端优化对于 C 函数的调用。值得注意的是,属性宏 @FastNative 只能用于 foreign 声明的函数。
@FastNative使用限制:
开发者在使用 @FastNative 修饰 foreign 函数时,应确保对应的C 函数满足以下两点要求。
- 首先,函数的整体执行时间不宜太长;
- 其次,函数内部不能调用仓颉方法。
3.2.3 自定义注解
自定义注解机制用来让反射获取标注内容,目的是在类型元数据之外提供更多的有用信息,以支持更复杂的逻辑。
开发者可以通过自定义类型标注 @Annotation 方式创建自己的自定义注解。@Annotation 只能修饰 class,并且不能是 abstract 或 open 或 sealed 修饰的 class。当一个 class 声明它标注了 @Annotation,那么它必须要提供至少一个 const init 函数,否则编译器会报错。
下面的例子定义了一个自定义注解 @Version,并用其修饰 A, B 和 C。在 main 中,通过反射获取到类上的 @Version 注解信息,并将其打印出来。
具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。

自定义注解在使用时必须使用 const init 构建出合法的实例。注解声明语法与声明宏语法一致,后面的[ ]括号中需要按顺序或命名参数规则传入参数,且参数必须是 const 表达式。
下面的例子中定义了一个拥有无参 const init 的自定义注解 @Marked,使用时 @Marked 和 @Marked[] 这两种写法均可。
具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。

Annotation 不会被继承,因此一个类型的注解元数据只会来自它定义时声明的注解。如果需要父类型的注解元数据信息,需要开发者自己用反射接口查询。
下面的例子中,A 被 @Marked 注解修饰,B 继承 A,但是 B 没有 A 的注解。
具体代码操作如下:
Step1:复制以下代码,替换main.cj文件中的代码。(保留package)
Step2:点击编辑器右上角运行按钮直接运行,终端窗口可以看到打印内容。

至此,仓颉之反射和注解的神秘力量案例内容已全部完成。
如果想了解更多仓颉编程语言知识可以访问: https://cangjie-lang.cn/


所有评论(0)