声明引用与反射
Norm 把类型、字段和 callable 的引用绑定到 Core 声明 identity。重命名会由工具链更新引用;目标被删除、不可见或重载无法唯一确定时,编译失败。运行时 API 不接受字符串声明名或公开 ordinal。
引用语法
| 目标 | 语法 | 类型 |
|---|---|---|
| 类型 | User.class | Class<User> |
| nullable 或泛型类型 | String?.class、List<String>.class | Class<String?>、Class<List<String>> |
| 字段 | User.id.field | Field<User, UserId> |
| 顶层函数 | findUser.function | Function<User(UserId)> |
| 未绑定方法 | UserService.findUser.function | Function<User(UserService, UserId)> |
| 绑定方法 | service.findUser | Function<User(UserId)> |
.function 指向声明;普通成员访问则产生可调用的绑定函数值。未绑定方法把名为 this 的 owner 参数放在签名首位,调用仍执行动态分派。
类型化 metadata
| 类型 | 稳定能力 |
|---|---|
Class<T> | name()、isValue()、annotation<A>()、fields()、functions()、constructors() |
Field<Owner, Value> | name()、type()、owner()、annotation<A>()、isPublic()、hasAnnotation(Class<A>)、identity(Owner)、read(Owner)、bind(Owner)、write(receiver: Owner, value: Value)、copy(target: Owner, source: Owner) |
Function<Signature> | name()、owner()、parameters()、annotation<A>() |
Parameter<Value> | name()、type()、function()、annotation<A>() |
Constructor<T> | owner() |
Field<Owner, Value>.type() 返回 Class<Value>,owner() 返回描述符中的 Class<Owner>。通过 Class<T>.fields() 枚举继承字段时,Owner 是当前的 T 视图;直接写 Base.value.field 时则是 Base。read(receiver: ...) 要求一个 Owner 实例,并以字段的精确 Value 类型返回值。
Class<T>.fields() 返回 List<Field<T, ?>>,functions() 返回 List<Function<?>>,constructors() 返回 List<Constructor<T>>。每个重载都是独立元素;异构集合使用 ? 隐藏不同的字段值类型或函数签名。
顶层函数和绑定方法可直接放入 List<Function<?>>;存在重载时先用精确函数类型选择声明。函数与参数的 annotation<A>() 查询 RuntimeRetention 注解,并与拦截器共享同一注解实例。通过序列化协议执行异构函数见 JSON API。
计算属性不产生存储字段,因此不增加 fields() 的条目。访问器进入 functions(),getter 和 setter 保留同一属性名及各自的 callable identity,参数列表包含接收者;验证见 PropertyExecutionTest.reflectsPropertyAccessorsWithoutInventingStorageFields。
运行时对象与字段复制
classOf(value) 返回对象实际类型的 Class<T>,其中 T 保留实参的静态类型。通过接口或父类持有对象时,fields() 仍枚举实际类型的字段。Class<?> 描述符的相等性和哈希只取决于所表示的类型,不取决于调用处的静态视图。isValue() 表示该类型是否具有值语义。
Field.bind(receiver) 产生 FieldHandle<Value>,持有可变 class 实例和字段位置。句柄可返回、存入对象并被闭包捕获;read() 读取当前值,write(value) 使用正常字段写入路径。同一对象的同一字段句柄相等,不同对象的字段句柄不相等。它不捕获局部变量位置,也不改变 ref<T> 的词法生命周期规则。 当期望类型为 FieldHandle<T> 时,可直接传入可访问的 class 存储字段;例如 binding(model.title) 自动捕获字段,接收者只求值一次。已有句柄保持原样传递,泛型调用可从字段类型推导 T。局部变量、计算属性、value 字段和 null-safe 字段访问不进行这种转换。
Field.write(receiver: ..., value: ...) 按字段的精确值类型写入可变 class,复用正常字段写入、注解拦截和变化通知;运行时校验接收者和实际字段类型。异构 Field<T, ?> 不能写入任意值。
导入 std.observation.onChange 后,可使用 model.title.onChange { ... } 订阅字段赋值。该 extension 的接收者是 FieldHandle<T>,复用字段捕获;回调参数为 oldValue、newValue,保留字段的 nullable 类型。订阅不立即执行,按字段值相等规则过滤重复赋值;返回 Resource,close() 可重复调用并停止订阅。
订阅自动接入创建时的资源归属上下文。GUI 组件提供该上下文,因此在 init() 或组件回调内注册的订阅会随组件销毁释放,变化回调也恢复原组件上下文;提前 close() 会立即解除归属。
通知携带该次变化的旧值和新值快照,回调内再次修改不会覆盖外层通知的参数。订阅回调抛出普通异常时,其他有效订阅仍收到本次变化,之后向修改调用方传播异常。
集合字段的整体替换、原地修改及嵌套值容器修改均可被观察;相等替换和没有改变内容的操作不通知。替换字段或嵌套容器后,订阅跟随当前字段中的值;复制出的集合不继承订阅。class 元素内部字段的修改不属于容器结构变化,应订阅该对象的字段。实现与执行覆盖见 FieldHandleExecutionTest。
Field.isPublic() 反映字段声明的可见性。Field.copy(target: ..., source: ...) 在相同字段描述符约束下复制字段,执行正常字段写入、注解拦截和变化通知;运行时检查两端对象的类型及泛型实参。目标必须是可变 class,不能通过反射修改 value。异构字段集合可以直接调用 copy,无需将字段值降为 Any。
运行验证见 ObjectReflectionExecutionTest。
Field.hasAnnotation(Class<A>) 查询运行时保留的注解是否属于指定注解类型或实现指定接口,不实例化注解。std.annotation.IdentityField 是身份字段的通用标记。
Field.identity(receiver) 返回不透明值类型 FieldIdentity,将 owner 类型、字段声明和字段值快照作为相等性及哈希依据,不经过字符串转换。字段值遵循 Norm 的值/对象身份语义;空值不构成身份。异构反射字段可直接生成身份,无须公开隐藏的字段值类型。验证见 FieldIdentityExecutionTest。
这个可执行示例组合了字段句柄、直接赋值、列表原位变更与订阅关闭:
import std.observation.onChange
class Board {
List<String> titles = []
Map<String, Integer> counts = Map<String, Integer>()
String selected = "none"
}
main() {
Board board = Board()
FieldHandle<String> current = Board.selected.field.bind(board)
var selected = board.selected.onChange {
printLine("selected: " + oldValue + " -> " + newValue)
}
var titles = board.titles.onChange {
printLine("titles: ${oldValue.size()} -> ${newValue.size()}")
}
var counts = board.counts.onChange {
printLine("counts: ${oldValue.size()} -> ${newValue.size()}")
}
board.titles.add("Learn Norm")
board.titles.add("Build app")
current.write("Learn Norm")
board.selected = "Learn Norm"
board.titles[0] = "Learn more Norm"
board.counts.put(key: "open", value: 1)
board.counts.put(key: "open", value: 1)
board.counts["open"] = 2
printLine(current.read())
printLine(board.titles[0])
selected.close()
titles.close()
counts.close()
board.titles.add("Publish")
current.write("Publish")
printLine(board.titles.size())
}titles: 0 -> 1
titles: 1 -> 2
selected: none -> Learn Norm
titles: 2 -> 2
counts: 0 -> 1
counts: 1 -> 1
Learn Norm
Learn more Norm
3重载
将重载声明引用赋给精确函数类型时,编译器使用期望签名选出唯一声明:
Function<User(UserService, UserId)> lookup = UserService.findUser.functionClass<T>.functions() 和 constructors() 则保留所有重载,供 metadata 查询。Function<?> 不知道可调用签名,因此不能直接执行。
实现真相源
反射成员和精确类型由 BuiltinCatalog 定义,二进制 metadata 中的声明引用由 CoreAnnotationReference 表示。