当基础的可见性(第4.4.1节)选项不满足需求时可以使用访问控制。访问控制在类层面上和字段层面上都适用,其中涉及两个方向上的访问控制:
Access control can be used if the basic visibility (4.4.1) options are not sufficient. It is applicable at class-level and at field-level and knows two directions:
允许访问:目标可通过 :allow(target)
元数据(6.9)提供给一个给定的类或字段进行访问。
强制访问:可在一个类或字段中通过 :access(target)
对给定目标进行强制访问。
Allowing access: The target is granted access to the given class or field by using the :allow(target) metadata (6.9). Forcing access: A target is forced to allow access to the given class or field by using the :access(target) metadata (6.9).
其中的 target 是一个点路径(dot-path),在不同语境下它可能是:
- 一个类字段,
- 一个类或者一个抽象类型,
- 或者是一个包
In this context, a target can be the dot-path (3.7) to
- a class field,
- a class or abstract type, or
- a package.
target 与当前上下文的导入无关,所以必须是一个完整的点路径进行表示。
Target does not respect imports, so the fully qualified path has to be used.
如果被访问控制所修饰的是一个类或者一个抽象类型,访问控制会被扩展至该类型的所有字段上。同样地,如果它是一个包,访问控制将会扩展至这个包内所有的类型、以及这些类型的所有字段上。
If it is a class or abstract type, access modification extends to all fields of that type. Likewise, if it is a package, access modification extends to all types of that package and recursively to all fields of these types.
@:allow(Main)
class MyClass {
static private var foo: Int;
}
class Main {
static public function main() {
MyClass.foo;
}
}
这里, MyClass.foo
字段可以在 main
方法中被访问,因为 MyClass
被 @:allow(Main)
所修饰。你也可以将修饰改为 @:allow(Main.main)
,而且这两种修饰也都可以修饰于字段 foo
之上而不是类上:
Here, MyClass.foo can be accessed from the main-method because MyClass is annotated with @:allow(Main). This would also work with @:allow(Main.main) and both versions could alternatively be annotated to the field foo instead of the class MyClass:
class MyClass {
@:allow(Main.main)
static private var foo: Int;
}
class Main {
static public function main() {
MyClass.foo;
}
}
而如果一个类的设计不允许被修饰为这类访问控制,那么可以使用强制访问来进行访问:
If a type cannot be modified to allow this kind of access, the accessing method may force access:
class MyClass {
static private var foo: Int;
}
class Main {
@:access(MyClass.foo)
static public function main() {
MyClass.foo;
}
}
通过 @access(MyClass.foo)
访问控制修饰使得 main
方法可以越过 foo
字段的可见性修饰。
The @:access(MyClass.foo) annotation effectively subverts the visibility of the foo field within the main-method.
元数据的选择
花絮:元数据的选择 访问控制的特性使用 Haxe 的元数据提供,而不是通过额外的语法提供。有如下几个理由: 额外的语法通常使得语言解析变得更加复杂,同时会导致加入太多关键字。 额外的语法需要另外的学习成本。 元数据语法足够灵活,且可以通过它来实现这一需要。 元数据可以通过Haxe的宏进行 访问/生成/修改。
当然,使用元数据语法的弊端是,如果元数据的关键字拼写错误(例如@:acesss)或者类/包名拼写错误,将不会得到错误报告。尽管如此,如果你尝试访问一个不被允许访问的私有字段时,编译器会报错,因为这不可能是一个不被觉察的错误。
Trivia: On the choice of metadata The access control language feature uses the Haxe metadata syntax instead of additional language-specific syntax. There are several reasons for that:
- Additional syntax often adds complexity to the language parsing, and also adds (too) many keywords.
- Additional syntax requires additional learning by the language user,whereas metadata syntax is something that is already known.
- The metadata syntax is flexible enough to allow extension of this feature.
- The metadata can be accessed/generated/modified by Haxe macros. Of course, the main drawback of using metadata syntax is that you get no error report in case you misspell either the metadata key (@:acesss for instance) or the class/package name. However, with this feature you will get an error when you try to access a private field that you are not allowed to, therefore there is no possibility for silent errors.
Haxe 3.1.0 以后
Since Haxe 3.1.0
如果允许访问一个 接口(第2.3.3节),它延伸到所有实现这个接口的类:
If access is allowed to an interface(2.3.3),it extends to all classes implementing that interface:
class MyClass {
@:allow(I)
static private var foo: Int;
}
interface I { }
class Main implements I {
static public function main() {
MyClass.foo;
}
}
对于访问授权父类,也是同样的,这种情况会延伸到所有的子类:
This is also true for access granted to parent classes, in which case it extends to all child classes.
破坏性功能
花絮:破坏性功能 子类和实现类的访问扩展只支持Haxe 3.0以后。当编写这本手册时发现这部分的访问控制实现是容易缺失的。 [warning] Trivia: Broken feature Access extension to child classes and implementing classes was supposed to work in Haxe 3.0 and even documented accordingly. While writing this manual it was found that this part of the access control implementation was simply missing.