本节内容:
- 可见性
- inline修饰符
- dynamic修饰符
- override修饰符
- static修饰符
字段默认为 private ,意味着只有类和它的子类可以访问它们。它们可以被声明为公共字段,通过使用 public 访问修饰符,使它们可以在各处访问。
Fields are by default private, meaning that only the class and its sub-classes may access them. They can be made public by using the public access modifier, allowing access from anywhere.
class MyClass {
static public function available() {
unavailable();
}
static private function unavailable() { }
}
class Main {
static public function main() {
MyClass.available();
// Cannot access private field unavailable
MyClass.unavailable();
}
}
MyClass类的可用字段的访问允许从Main中访问,因为它表示为 public。然而,当访问不可用的字段访问可以从类MyClass内部,但是不能从Main中访问,因为它是private(明确的私有声明,尽管这个标识符在这里是多余的)
Access to field available of class MyClass is allowed from within Main because it is denoted as being public. However, while access to field unavailable is allowed from within class MyClass, it is not allowed from within class Main because it is private (explicitly, although this identifier is redundant here).
例子展示了static字段的可见性,但是成员字段的规则是等价的。下面的示例展示了当继承(第2.3.2节)被使用时的可见性行为。
The example demonstrates visibility through static fields, but the rules for member fields are equivalent. The following example demonstrates visibility behavior for when inheritance (2.3.2) is involved.
class Base {
public function new() { }
private function baseField() { }
}
class Child1 extends Base {
private function child1Field() { }
}
class Child2 extends Base {
public function child2Field() {
var child1 = new Child1();
child1.baseField();
// Cannot access private field child1Field
child1.child1Field();
}
}
class Main {
static public function main() { }
}
我们可以看到访问 child1.baseField() 是允许在Child2类中访问,即使child1是不同的类型,Child1 。这是因为字段被定义在它们的通用祖先类Base,相反的,字段 Child1Field不能被从Child2中访问。
We can see that access to child1.baseField() is allowed from within Child2 even though child1 is of a different type,Child1. This is because the field is defined on their common ancestor class Base, contrary to field child1Field which can not be accessed from within Child2.
省略可见性的修饰符通常默认可见性为 private,但是有例外它会变成 public:
Omitting the visibility modifier usually defaults the visibility to private, but there are exceptions where it becomes public instead:
- 如果类被声明为 extern
- 如果字段被声明在一个接口(第2.3.3节)
- 如果字段重载(第4.3.1节)了一个 public 字段
- If the class is declared as extern.
- If the field is declared on an interface (2.3.3).
- If the field overrides (4.3.1) a public field.
Protected
花絮:Protected Haxe没有类似Java和C++等其它面向对象语言中的protected概念。然而,它的private行为等同于那些语言的protected行为,所以Haxe实际上缺少的是那些语言中的 private 行为。 [warning] Trivia: Protected Haxe has no notion of a protected keyword known from Java, C++ and other object-oriented languages. However, its private behavior is equal to those language’s protected behavior, so Haxe actually lacks their real private behavior.
inline 关键字允许函数体被直接插入到它们调用位置。这是一个强大的优化工具,但是应该审慎使用,并不是所有函数都适用inline行为。下面的例子演示了基本的用法:
The inline keyword allows function bodies to be directly inserted in place of calls to them. This can be a powerful optimization tool,but should be used judiciously as not all functions are good candidates for inline behavior. The following example demonstrates the basic usage:
class Main {
static inline function mid(s1:Int, s2:Int) {
return (s1 + s2) / 2;
}
static public function main() {
var a = 1;
var b = 2;
var c = mid(a, b);
}
}
生成的JavaScript输出揭示了内联的效果:
The generated JavaScript output reveals the effect of inline:
(function () { "use strict";
var Main = function() { }
Main.main = function() {
var a = 1;
var b = 2;
var c = (a + b) / 2;
}
Main.main();
})();
显然,字段mid被生成的函数体(s1 + s2)/2替换掉了调用 mid(a,b),s1被替换为 a ,s2被替换为b。这可以避免一个函数调用,根据目标和出现的频率,可以产生显著的性能改进。
As evident, the function body (s1 + s2) / 2 of field mid was generated in place of the call to mid(a, b), with s1 being replaced by a and s2 being replaced by b. This avoids a function call which,depending on the target and frequency of occurrences,may yield noticeable performance improvements.
并不总是容易判断是否一个函数要限定为内联函数。没有编写表达式的短函数(如a=形式的赋值)通常是一个好的选择,但是有时候更复杂的函数也可以使用内联。然而,在一些情况下内联可以实际上损害部分性能,例如,因为编译器必须创建临时变量服务于复杂的表达式。
It is not always easy to judge if a function qualifies for being inline. Short functions that have no writing expressions (such as a = assignment) are usually a good choice, but even more complex functions can be candidates. However, in some cases inlining can actually be detrimental to performance, e.g. because the compiler has to create temporary variables for complex expressions.
内联并不保证执行。编译器可能由于多种原因取消内联,用户也可以通过 --no-inline 命令行参数来禁止内联。唯一的例外是如果类是 extern(第6.2节)或者如果泪字段有 :extern 元数据(第6.9节),这种情况内联被禁止。如果她不能被执行,编译器发出一个错误。
Inline is not guaranteed to be done. The compiler might cancel inlining for various reasons or a user could supply the --no-inline command line argument to disable inlining. The only exception is if the class is extern (6.2) or if the class field has the :extern metadata (6.9), in which case inline is forced. If it cannot be done, the compiler emits an error.
重要的是依赖内联时要记得这个:
It is important to remember this when relying on inline:
class Main {
public static function main () { }
static function test() {
if (Math.random() > 0.5) {
return "ok";
} else {
error("random failed");
}
}
@:extern static inline function error(s:String) {
throw s;
}
}
如果正确的调用error是内联的,程序编译正确,因为控制流检查器满意内联的throw(第5.22节)表达式。如果内联部执行,编译器只发现一个error函数的调用并发出错误 A return is missing here。
If the call to error is inlined the program compiles correctly because the control flow checker is satisfied due to the inlined throw (5.22) expression. If inline is not done,the compiler only sees a function call to error and emits the error A return is missing here.
The inline
keyword can also be applied to variables, but only when used together with static
. An inline variable must be initialized to a constant, otherwise the compiler emits an error. The value of the variable is used everywhere in place of the variable itself.
The following code demonstrates the usage of an inline variable:
class Main {
static inline final language = "Haxe";
static public function main() {
trace(language);
}
}
The generated JavaScript shows that the language
variable is not present anymore:
(function ($global) { "use strict";
var Main = function() { };
Main.main = function() {
console.log("root/program/Main.hx:5:","Haxe");
};
Main.main();
})({});
Note that even though we call such kind of fields "variables", inline variables can never be reassigned as the value must be known at compile-time to be inlined at the place of usage. This makes inline variables a subset of final
fields, hence the usage of the final
keyword in the code example above.
Prior to Haxe 4, there was no
final
keyword. The inline variables feature however was present for a long time, using thevar
keyword instead offinal
. Usinginline var
still works in Haxe 4 but might be deprecated in the future, becausefinal
is more appropriate.
方法可以通过 dynamic 关键字 使它们可绑定(重绑定):
Methods can be denoted with the dynamic keyword to make them (re-)bindable:
class Main {
static dynamic function test() {
return "original";
}
static public function main() {
trace(test()); // original
test = function() { return "new"; }
trace(test()); // new
}
}
第一次调用 test() 调用了原来的函数,返回字符串 “original”。下一行,test被分配了一个新的函数。这恰恰是dynamic做到的:函数字段可以被分配一个新的函数。其结果是,下一次调用 test() 返回字符串 “new” 。
The first call to test() invokes the original function which returns the String "original". In the next line, test is assigned a new function. This is precisely what dynamic allows: Function fields can be assigned a new function. As a result,the next invocation of test() returnsthe String "new".
动态字段因为明显的理由不能内联:内联是在编译时执行,动态函数必须被在运行时决定。
Dynamic fields cannot be inline for obvious reasons: While inlining is done at compiletime, dynamic functions necessarily have to be resolved at runtime.
访问修饰符 override 要求被修饰的字段声明也同时出现在父类(第2.3.2节)中。它的目的是确保类的作者意识到 override 因为这不能总是被明显的在庞大的类层级中。此外,如果字段修饰了override关键字而实际上没有重写任何东西(例如由于字段名的拼写错误导致)会引发一个错误。
The access modifier override is required when a field is declared which also exists on a parent class(2.3.2). Its purpose is to ensure that the author of a class is aware of the override as this may not always be obvious in large class hierarchies. Likewise, having override on a field which does not actually override anything (e.g. due to a misspelled field name) triggers an error.
重载字段的效果在重载方法(第4.3.1节)详述。这个修饰符只允许用于方法(第4.3节)字段。
The effects of overriding fields are detailed in Overriding Methods (Section 4.3.1). This modifier is only allowed on method (4.3) fields.
所有的字段除非修饰符标记为 static 否则都是成员字段。Static字段用在类上,就像非静态字段用在类的实例上。
所有的字段除非修饰符标记为 static 否则都是成员字段。static字段通过类使用,而非static字段通过类的实例使用。
All fields are member fields unless the modifier static is used. Static fields are used “on the class” whereas non-static fields are used “on a class instance”:
class Main {
static function main() {
Main.staticField; // static read
Main.staticField = 2; // static write
}
static var staticField:Int;
}
静态变量(第4.1节)和属性(第4.2节)字段可以使用任意的初始化表达式(第5章).
Static variable (4.1) and property (4.2) fields can have arbitrary initialization expressions (5).