Annotations
Inspect one language lane at a time so line-level text and code deltas stay readable.
Diff Lane
English
0 modified sections0 code block delta0 anchor delta
Diff Lane
中文
3 modified sections0 code block delta0 anchor delta
modified确保正确使用整数运算溢出策略的内置编译标记text+35 lines, -12 lines
v1.0.5
Section Text
1
仓颉中提供三种内置编译标记来控制整数溢出的处理策略,即 `@OverflowThrowing`,`@OverflowWrapping` 和 `@OverflowSaturating` ,这些编译标记当前只能标记于函数声明之上,作用于函数内的整数运算和整型转换。它们分别对应以下三种溢出处理策略:2
3
1. 抛出异常(throwing):当整数运算溢出时,抛出异常。4
5
<!-- compile -->6
7
```cangjie8
@OverflowThrowing9
func add(a: Int8, b: Int8){10
return a + b11
}12
main() {13
add(100,29)14
/* 100 + 29 在数学上等于 129,15
* 在 Int8 的表示范围上发生了上溢出,16
* 程序抛出异常17
*/18
}19
```20
21
需要注意的是,对于整数溢出行为是 throwing 的场景,若整数溢出可提前在编译期检测出来,则编译器会直接给出报错。22
23
<!-- compile.error -->24
25
```cangjie26
@OverflowThrowing27
main() {28
let res: Int8 = Int8(100) + Int8(29) // Error, arithmetic operation '+' overflow29
// 100 + 29 在数学上等于 129,在 Int8 的表示范围上发生了上溢出,编译器检测出来并报错30
let con: UInt8 = UInt8(-132) // Error, integer type conversion overflow31
/* -132 在 UInt8 的表示范围上发生了下溢出,32
* 程序抛出异常33
*/34
}35
```36
37
2. 高位截断(wrapping):当整数运算的结果超出用于接收它的内存空间所能表示的数据范围时,则截断超出该内存空间的部分。38
39
<!-- compile -->40
41
```cangjie42
@OverflowWrapping43
main() {44
let res: Int8 = Int8(105) * Int8(4)45
/* 105 * 4 在数学上等于 420,46
* 对应的二进制为 1 1010 0100,47
* 超过了用于接收该结果的 8 位内存空间,48
* 截断后的结果在二进制上表示为 1010 0100,49
* 对应为有符号整数 -9250
*/51
let temp: Int16 = Int16(-132)52
let con: UInt8 = UInt8(temp)53
/* -132 对应的二进制为 1111 1111 0111 1100,54
* 超过了用于接收该结果的 8 位内存空间,55
* 截断后的结果在二进制上表示为 0111 110056
* 对应为有符号整数 12457
*/58
}59
```60
61
3. 饱和(saturating):当整数运算溢出时,选择对应固定精度的极值作为结果。62
63
<!-- compile -->64
65
```cangjie66
@OverflowSaturating67
main() {68
let res: Int8 = Int8(-100) - Int8(45)69
/* -100 - 45 在数学上等于 -145,70
* 在 Int8 的表示范围上发生了下溢出,71
* 选择 Int8 的最小值 -128 作为结果72
*/73
let con: Int8 = Int8(1024)74
/* 1024 在 Int8 的表示范围上发生了上溢出,75
* 选择 Int8 的最大值 127 作为结果76
*/77
}78
```79
80
默认情况下(即未标注该类内置编译标记时),采取抛出异常(`@OverflowThrowing`)的处理策略。81
82
实际情况下需要根据业务场景的需求正确选择溢出策略。例如要在 `Int32` 上实现某种安全运算,使得计算结果和计算过程在数学上相等,就需要使用抛出异常的策略。83
84
【反例】85
86
<!-- compile -->87
88
89
该错误例子使用了高位截断的溢出策略,比如当传入的参数 `a` 和 `b` 较大导致结果溢出时,会产生高位截断的情况,导致函数返回结果和计算表达式 `a + b` 在数学上不是相等关系。90
91
【正例】92
93
<!-- run -->94
95
96
该正确例子使用了抛出异常的溢出策略,当传入的参数 `a` 和 `b` 较大导致整数溢出时,`operation` 函数会抛出异常。97
98
下面总结了可能造成整数溢出的数学操作符。99
100
| 操作符 | 溢出 | 操作符 | 溢出 | 操作符 | 溢出 | 操作符 | 溢出 |101
|:----:|:--:|:--------------------:|:--:|:-------------------:|:--:|:----:|:--:|102
| `+` | Y | `-=` | Y | `<<` | N | `<` | N |103
| `-` | Y | `*=` | Y | `>>` | N | `>` | N |104
| `*` | Y | `/=` | Y | `&` | N | `>=` | N |105
| `/` | Y | `%=` | N | <code>|</code> | N | `<=` | N |106
| `%` | N | `<<=` | N | `^` | N | `==` | N |107
| `++` | Y | `>>=` | N | `**=` | Y | | |108
| `--` | Y | `&=` | N | `!` | N | | |109
| `=` | N | <code>|=</code> | N | `!=` | N | | |110
| `+=` | Y | `^=` | N | `**` | Y | | |Code 1 · cangjie
1
// 计算结果被高位截断2
@OverflowWrapping3
func operation(a: Int32, b: Int32): Int32 {4
a + b // No exception will be thrown when overflow occurs5
}Code 2 · cangjie
1
// 安全2
@OverflowThrowing3
func operation(a: Int32, b: Int32): Int32 {4
a + b5
}6
7
main(): Int64 {8
try {9
operation(Int32.Max, 1)10
} catch (e: ArithmeticException) {11
println(e.message)12
//Handle error13
}14
015
}v1.1.0
Section Text
1
仓颉中提供三种内置编译标记来控制整数溢出的处理策略,即 `@OverflowThrowing`,`@OverflowWrapping` 和 `@OverflowSaturating` ,这些编译标记当前只能标记于函数声明之上,作用于函数内的整数运算和整型转换。它们分别对应以下三种溢出处理策略:2
3
1. 抛出异常(throwing):当整数运算溢出时,抛出异常。4
5
<!-- compile -->6
7
```cangjie8
@OverflowThrowing9
func add(a: Int8, b: Int8){10
return a + b11
}12
main() {13
add(100,29)14
/* 100 + 29 在数学上等于 129,15
* 在 Int8 的表示范围上发生了上溢出,16
* 程序抛出异常17
*/18
}19
```20
21
需要注意的是,对于整数溢出行为是 throwing 的场景,若整数溢出可提前在编译期检测出来,则编译器会直接给出报错。22
23
<!-- compile.error -->24
25
```cangjie26
@OverflowThrowing27
main() {28
let res: Int8 = Int8(100) + Int8(29) // Error, arithmetic operation '+' overflow29
// 100 + 29 在数学上等于 129,在 Int8 的表示范围上发生了上溢出,编译器检测出来并报错30
let con: UInt8 = UInt8(-132) // Error, integer type conversion overflow31
/* -132 在 UInt8 的表示范围上发生了下溢出,32
* 程序抛出异常33
*/34
}35
```36
37
2. 高位截断(wrapping):当整数运算的结果超出用于接收它的内存空间所能表示的数据范围时,则截断超出该内存空间的部分。38
39
<!-- compile -->40
41
```cangjie42
@OverflowWrapping43
main() {44
let res: Int8 = Int8(105) * Int8(4)45
/* 105 * 4 在数学上等于 420,46
* 对应的二进制为 1 1010 0100,47
* 超过了用于接收该结果的 8 位内存空间,48
* 截断后的结果在二进制上表示为 1010 0100,49
* 对应为有符号整数 -9250
*/51
let temp: Int16 = Int16(-132)52
let con: UInt8 = UInt8(temp)53
/* -132 对应的二进制为 1111 1111 0111 1100,54
* 超过了用于接收该结果的 8 位内存空间,55
* 截断后的结果在二进制上表示为 0111 110056
* 对应为有符号整数 12457
*/58
}59
```60
61
3. 饱和(saturating):当整数运算溢出时,选择对应固定精度的极值作为结果。62
63
<!-- compile -->64
65
```cangjie66
@OverflowSaturating67
main() {68
let res: Int8 = Int8(-100) - Int8(45)69
/* -100 - 45 在数学上等于 -145,70
* 在 Int8 的表示范围上发生了下溢出,71
* 选择 Int8 的最小值 -128 作为结果72
*/73
let con: Int8 = Int8(1024)74
/* 1024 在 Int8 的表示范围上发生了上溢出,75
* 选择 Int8 的最大值 127 作为结果76
*/77
}78
```79
80
默认情况下(即未标注该类内置编译标记时),采取抛出异常(`@OverflowThrowing`)的处理策略。81
82
实际情况下需要根据业务场景的需求正确选择溢出策略。例如要在 `Int32` 上实现某种安全运算,使得计算结果和计算过程在数学上相等,就需要使用抛出异常的策略。83
84
【反例】85
86
<!-- compile -->87
88
89
该错误例子使用了高位截断的溢出策略,比如当传入的参数 `a` 和 `b` 较大导致结果溢出时,会产生高位截断的情况,导致函数返回结果和计算表达式 `a + b` 在数学上不是相等关系。90
91
【正例】92
93
<!-- run -->94
95
96
该正确例子使用了抛出异常的溢出策略,当传入的参数 `a` 和 `b` 较大导致整数溢出时,`operation` 函数会抛出异常。97
98
以下表格汇总了仓颉中各类操作符是否可能会引发整数溢出的情况。99
100
| 操作符类型 | 操作符 | 是否可能引发整数溢出 |101
| :------------------: | :------------------: | -------------------- |102
| 算术操作符 | + | 是 |103
| 算术操作符 | - | 是 |104
| 算术操作符 | * | 是 |105
| 算术操作符 | / | 是 |106
| 算术操作符 | % | 否 |107
| 算术操作符 | ** | 是 |108
| 自增操作符 | ++ | 是 |109
| 自减操作符 | -- | 是 |110
| 位操作符 | ! | 否 |111
| 位操作符 | & | 否 |112
| 位操作符 | <code>|</code> | 否 |113
| 位操作符 | ^ | 否 |114
| 位操作符 | << | 是 |115
| 位操作符 | >> | 否 |116
| 关系操作符 | != | 否 |117
| 关系操作符 | < | 否 |118
| 关系操作符 | > | 否 |119
| 关系操作符 | >= | 否 |120
| 关系操作符 | <= | 否 |121
| 关系操作符 | == | 否 |122
| 赋值操作符 | = | 否 |123
| 复合赋值操作符 | += | 是 |124
| 复合赋值操作符 | -= | 是 |125
| 复合赋值操作符 | *= | 是 |126
| 复合赋值操作符 | /= | 是 |127
| 复合赋值操作符 | %= | 否 |128
| 复合赋值操作符 | **= | 是 |129
| 复合赋值操作符 | <<= | 是 |130
| 复合赋值操作符 | >>= | 否 |131
| 复合赋值操作符 | &= | 否 |132
| 复合赋值操作符 | <code>|=</code> | 否 |133
| 复合赋值操作符 | ^= | 否 |Code 1 · cangjie
1
// 计算结果被高位截断2
@OverflowWrapping3
func operation(a: Int32, b: Int32): Int32 {4
a + b // No exception will be thrown when overflow occurs5
}Code 2 · cangjie
1
// 安全2
@OverflowThrowing3
func operation(a: Int32, b: Int32): Int32 {4
a + b5
}6
7
main(): Int64 {8
try {9
operation(Int32.Max, 1)10
} catch (e: ArithmeticException) {11
println(e.message)12
//Handle error13
}14
015
}modified测试框架内置编译标记text+2 lines, -1 line
v1.0.5
Section Text
1
在测试中使用 mock 时,当 mock 的对象是与静态和顶级声明相关内容时,需要通过测试框架内置编译标记来指示编译器做一些准备工作,才能正常使用 mock。2
3
测试框架内置编译标记 `@EnsurePreparedToMock` 只能在 lambda 表达式上使用,lambda 表达式调用静态和顶级声明作为其最后一个表达式,然后编译器将准备这个声明以供 mock。4
5
例如:6
7
<!-- run -pkg1 -->8
<!-- cfg="-p prod --mock=on --output-type=dylib" -->9
10
11
<!-- run -pkg1 -->12
<!-- cfg="-lprod -L . --test" -->13
14
15
上述示例中,`ConfigureMock.stubFunction` 为函数 `test` 注册了一个桩,`returns` 为定义的桩设置返回值。16
17
> **注意:**18
>19
> 通常,标准库的 mock 接口可用于定义 mock 声明,并且在常规情况下不应直接使用此内置注释。相反,应该使用相应的标准库函数。这些标准库函数在内部使用 `@EnsurePreparedToMock`。20
21
使用 `@EnsurePreparedToMock` 注解的约束:22
23
- 仅当使用测试和 mock 相关编译选项进行编译时才允许使用(使用 `--test`/`--test-only` 和 `--mock=on`/`--mock=runtime-error` 编译选项)。24
- 只能应用于具有合适的最后一个表达式的 lambda。25
- lambda 的最后一个表达式应该是调用、成员访问或引用表达式,要求是:26
- 顶级函数或变量;27
- 静态函数、属性或字段;28
- foreign 声明;29
- 不是局部函数或变量;30
- 非私有声明;31
- 不是 const 表达式或声明;32
- 必须是来自通过 mock 模式构建的包的声明。Code 1 · cangjie
1
package prod2
3
public func test(a: String, b: String): String {4
a + b5
}Code 2 · cangjie
1
package test2
3
import prod.*4
import std.unittest.mock.*5
6
@Test7
public class TestA {8
@TestCase9
func case1(): Unit {10
{ =>11
let matcher0 = Matchers.eq("z")12
let matcher1 = Matchers.eq("y")13
let stubCall = @EnsurePreparedToMock { => return(test(matcher0.value(), matcher1.value())) }14
ConfigureMock.stubFunction(stubCall,[matcher0.withDescription(#"eq("z")"#), matcher1.withDescription(#"eq("y")"#)], Option<String>.None, "test", #"test("z", "y")"#, 15)15
}().returns("mocked value")16
println(test("z", "y")) // prints "mocked value"17
}18
}v1.1.0
Section Text
1
在测试中使用 mock 时,当 mock 的对象是与静态和顶级声明相关内容时,需要通过测试框架内置编译标记来指示编译器做一些准备工作,才能正常使用 mock。2
3
测试框架内置编译标记 `@EnsurePreparedToMock` 只能在 lambda 表达式上使用,lambda 表达式调用静态和顶级声明作为其最后一个表达式,然后编译器将准备这个声明以供 mock。4
5
例如:6
7
<!-- run -pkg1 -->8
<!-- cfg="-p prod --mock=on --output-type=dylib" -->9
10
11
<!-- run -pkg1 -->12
<!-- cfg="-lprod -L . --test" -->13
14
15
上述示例中,`ConfigureMock.stubFunction` 为函数 `test` 注册了一个桩,`returns` 为定义的桩设置返回值。16
17
> **注意:**18
>19
> 通常,标准库的 mock 接口可用于定义 mock 声明,并且在常规情况下不应直接使用此内置注释。相反,应该使用相应的标准库函数。这些标准库函数在内部使用 `@EnsurePreparedToMock`。20
21
使用 `@EnsurePreparedToMock` 注解的约束:22
23
- 仅当使用测试和 mock 相关编译选项进行编译时才允许使用(使用 `--test`/`--test-only` 和 `--mock=on`/`--mock=runtime-error` 编译选项)。24
- 只能应用于具有合适的最后一个表达式的 lambda。25
- lambda 的最后一个表达式应该是调用、成员访问或引用表达式,要求是:26
- 顶级函数或变量;27
- 扩展中的静态函数;28
- 类中的静态函数、属性或字段;29
- foreign 声明;30
- 不是局部函数或变量;31
- 非私有声明;32
- 不是 const 表达式或声明;33
- 必须是来自通过 mock 模式构建的包的声明。Code 1 · cangjie
1
package prod2
3
public func test(a: String, b: String): String {4
a + b5
}Code 2 · cangjie
1
package test2
3
import prod.*4
import std.unittest.mock.*5
6
@Test7
public class TestA {8
@TestCase9
func case1(): Unit {10
{ =>11
let matcher0 = Matchers.eq("z")12
let matcher1 = Matchers.eq("y")13
let stubCall = @EnsurePreparedToMock { => return(test(matcher0.value(), matcher1.value())) }14
ConfigureMock.stubFunction(stubCall,[matcher0.withDescription(#"eq("z")"#), matcher1.withDescription(#"eq("y")"#)], Option<String>.None, "test", #"test("z", "y")"#, 15)15
}().returns("mocked value")16
println(test("z", "y")) // prints "mocked value"17
}18
}modified自定义注解textcode+3 lines, -5 lines
v1.0.5
Section Text
1
自定义注解机制用来让反射(详见[反射章节](dynamic_feature.md))获取标注内容,目的是在类型元数据之外提供更多的有用信息,以支持更复杂的逻辑。2
3
开发者可以通过自定义类型标注 `@Annotation` 方式创建自己的自定义注解。`@Annotation` 只能修饰 `class`,并且不能是 `abstract` 或 `open` 或 `sealed` 修饰的 `class`。当一个 `class` 声明它标注了 `@Annotation`,那么它必须要提供至少一个 `const init` 函数,否则编译器会报错。4
5
下面的例子定义了一个自定义注解 `@Version`,并用其修饰 `A`, `B` 和 `C`。在 `main` 中,通过反射获取到类上的 `@Version` 注解信息,并将其打印出来。6
7
<!-- verify -->8
9
10
编译并执行上述代码,输出结果为:11
12
13
注解信息需要在编译时生成信息并绑定到类型上,自定义注解在使用时必须使用 `const init` 构建出合法的实例。注解声明语法与声明宏语法一致,后面的 `[]` 括号中需要按顺序或命名参数规则传入参数,且参数必须是 const 表达式,详见[常量求值章节](../function/const_func_and_eval.md)。对于拥有无参构造函数的注解类型,声明时允许省略中括号。14
15
下面的例子中定义了一个拥有无参 `const init` 的自定义注解 `@Marked`,使用时 `@Marked` 和 `@Marked[]` 这两种写法均可。16
17
<!-- verify -->18
19
20
编译并执行上述代码,输出结果为:21
22
23
对于同一个注解目标,同一个注解类不允许声明多次,即不可重复。24
25
<!-- verify.error -->26
<!-- cfg="--Marked" -->27
28
29
`Annotation` 不会被继承,因此一个类型的注解元数据只会来自它定义时声明的注解。如果需要父类型的注解元数据信息,需要开发者自己用反射接口查询。30
31
下面的例子中,`A` 被 `@Marked` 注解修饰,`B` 继承 `A`,但是 `B` 没有 `A` 的注解。32
33
<!-- verify -->34
35
36
编译并执行上述代码,输出结果为:37
38
39
自定义注解可以用在类型声明(`class`、`struct`、`enum`、`interface`)、成员函数/构造函数中的参数、构造函数声明、成员函数声明、成员变量声明、成员属性声明。也可以限制自己可以使用的位置,这样可以减少开发者的误用,这类注解需要在声明 `@Annotation` 时标注 `target` 参数,参数类型为 `Array<AnnotationKind>`。其中,`AnnotationKind` 是标准库中定义的 `enum`。当没有限定 target 的时候,该自定义注解可以用在以上全部位置。当限定 target 时,只能用在声明的列表中。40
41
<!-- compile -->42
43
44
下面的例子中,自定义注解通过 `target` 限定只能用在成员函数上,用在其他位置会编译报错。45
46
<!--compile.error -->Code 1 · cangjie
1
package pkg2
3
import std.reflect.TypeInfo4
5
@Annotation6
public class Version {7
let code: String8
const init(code: String) {9
this.code = code10
}11
}12
13
@Version["1.0"]14
class A {}15
16
@Version["1.1"]17
class B {}18
19
main() {20
let objects = [A(), B()]21
for (obj in objects) {22
let annOpt = TypeInfo.of(obj).findAnnotation<Version>()23
if (let Some(ann) <- annOpt) {24
println(ann.code)25
}26
}27
}Code 2 · text
1
1.02
1.1Code 3 · cangjie
1
package pkg2
3
import std.reflect.TypeInfo4
5
@Annotation6
public class Marked {7
const init() {}8
}9
10
@Marked11
class A {}12
13
@Marked[]14
class B {}15
16
main() {17
if (TypeInfo.of(A()).findAnnotation<Marked>().isSome()) {18
println("A is Marked")19
}20
if (TypeInfo.of(B()).findAnnotation<Marked>().isSome()) {21
println("B is Marked")22
}23
}Code 4 · text
1
A is Marked2
B is MarkedCode 5 · cangjie
1
@Marked2
@Marked // Error3
class A {}Code 6 · text
1
package pkg2
3
import std.reflect.TypeInfo4
5
@Annotation6
public class Marked {7
const init() {}8
}9
10
@Marked11
open class A {}12
13
class B <: A {}14
15
main() {16
if (TypeInfo.of(A()).findAnnotation<Marked>().isSome()) {17
println("A is Marked")18
}19
if (TypeInfo.of(B()).findAnnotation<Marked>().isSome()) {20
println("B is Marked")21
}22
}Code 7 · cangjie
1
A is MarkedCode 8 · cangjie
1
public enum AnnotationKind {2
| Type3
| Parameter4
| Init5
| MemberProperty6
| MemberFunction7
| MemberVariable8
}Code 9 · cangjie
1
@Annotation[target: [MemberFunction]]2
public class Marked {3
const init() {}4
}5
6
class A {7
@Marked // OK, member funciton8
func marked() {}9
}10
11
@Marked // Error, type12
class B {}v1.1.0
Section Text
1
自定义注解机制用来让反射(详见[反射章节](dynamic_feature.md))获取标注内容,目的是在类型元数据之外提供更多的有用信息,以支持更复杂的逻辑。2
3
开发者可以通过自定义类型标注 `@Annotation` 方式创建自己的自定义注解。`@Annotation` 只能修饰 `class`,并且不能是 `abstract` 或 `open` 或 `sealed` 修饰的 `class`。当一个 `class` 声明它标注了 `@Annotation`,那么它必须要提供至少一个 `const init` 函数,否则编译器会报错。4
5
开发者也可以使用 @!Annotation 的语法创建自定义注解。这是为后续功能预留的语法,当前版本下与 @Annotation 等效。6
7
下面的例子定义了一个自定义注解 `@Version`,并用其修饰 `A`, `B` 和 `C`。在 `main` 中,通过反射获取到类上的 `@Version` 注解信息,并将其打印出来。8
9
<!-- verify -->10
11
12
编译并执行上述代码,输出结果为:13
14
15
注解信息需要在编译时生成信息并绑定到类型上,自定义注解在使用时必须使用 `const init` 构建出合法的实例。注解声明语法与声明宏语法一致,后面的 `[]` 括号中需要按顺序或命名参数规则传入参数,且参数必须是 const 表达式,详见[常量求值章节](../function/const_func_and_eval.md)。对于拥有无参构造函数的注解类型,声明时允许省略中括号。16
17
下面的例子中定义了一个拥有无参 `const init` 的自定义注解 `@Marked`,使用时 `@Marked` 和 `@Marked[]` 这两种写法均可。18
19
<!-- verify -->20
21
22
编译并执行上述代码,输出结果为:23
24
25
`Annotation` 不会被继承,因此一个类型的注解元数据只会来自它定义时声明的注解。如果需要父类型的注解元数据信息,需要开发者自己用反射接口查询。26
27
下面的例子中,`A` 被 `@Marked` 注解修饰,`B` 继承 `A`,但是 `B` 没有 `A` 的注解。28
29
<!-- verify -->30
31
32
编译并执行上述代码,输出结果为:33
34
35
自定义注解可以用在类型声明(`class`、`struct`、`enum`、`interface`)、函数参数(包括全局函数、成员函数、构造函数中的参数)、构造函数声明、成员函数声明、成员变量声明、成员属性声明、枚举构造器、全局函数、全局变量、扩展声明。也可以限制自己可以使用的位置,这样可以减少开发者的误用,这类注解需要在声明 `@Annotation` 时标注 `target` 参数,参数类型为 `Array<AnnotationKind>`。其中,`AnnotationKind` 是标准库中定义的 `enum`。当没有限定 target 的时候,该自定义注解可以用在以上全部位置。当限定 target 时,只能用在声明的列表中。36
37
<!-- compile -->38
39
40
下面的例子中,自定义注解通过 `target` 限定只能用在成员函数上,用在其他位置会编译报错。41
42
<!-- compile.error -->Code 1 · cangjie
1
package pkg2
3
import std.reflect.TypeInfo4
5
@Annotation6
public class Version {7
let code: String8
const init(code: String) {9
this.code = code10
}11
}12
13
@Version["1.0"]14
class A {}15
16
@Version["1.1"]17
class B {}18
19
main() {20
let objects = [A(), B()]21
for (obj in objects) {22
let annOpt = TypeInfo.of(obj).findAnnotation<Version>()23
if (let Some(ann) <- annOpt) {24
println(ann.code)25
}26
}27
}Code 2 · text
1
1.02
1.1Code 3 · cangjie
1
package pkg2
3
import std.reflect.TypeInfo4
5
@Annotation6
public class Marked {7
const init() {}8
}9
10
@Marked11
class A {}12
13
@Marked[]14
class B {}15
16
main() {17
if (TypeInfo.of(A()).findAnnotation<Marked>().isSome()) {18
println("A is Marked")19
}20
if (TypeInfo.of(B()).findAnnotation<Marked>().isSome()) {21
println("B is Marked")22
}23
}Code 4 · text
1
A is Marked2
B is MarkedCode 5 · cangjie
1
package pkg2
3
import std.reflect.TypeInfo4
5
@Annotation6
public class Marked {7
const init() {}8
}9
10
@Marked11
open class A {}12
13
class B <: A {}14
15
main() {16
if (TypeInfo.of(A()).findAnnotation<Marked>().isSome()) {17
println("A is Marked")18
}19
if (TypeInfo.of(B()).findAnnotation<Marked>().isSome()) {20
println("B is Marked")21
}22
}Code 6 · text
1
A is MarkedCode 7 · cangjie
1
public enum AnnotationKind {2
| Type3
| Parameter4
| Init5
| MemberProperty6
| MemberFunction7
| MemberVariable8
| EnumConstructor9
| GlobalFunction10
| GlobalVariable11
| Extension12
| ...13
}Code 8 · cangjie
1
@Annotation[target: [MemberFunction]]2
public class Marked {3
const init() {}4
}5
6
class A {7
@Marked // OK, member funciton8
func marked() {}9
}10
11
@Marked // Error, type12
class B {}Code 9 · cangjie
1