多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[TOC] 实例字段一个对象中类的具象化特征。在类方法中,当前的对象被通过 关键字 `this` 引用。 # 函数 * * * * * 实例函数必须在类中定义,并使用如下的语法结构: ~~~ //访问修饰符可以是public,private或者 omitted //可选参数需要使用 ? 标记 public function fName(al : Type, ?a2 : Type) : Type { //... } ~~~ 第一个关键字是一个可选的访问修饰符,声明字段的可访问性。如果是 public ,那么这个字段可以在类声明的内部/外部调用,如果是 private或者当是 omitted,字段只能从内部访问。 ~~~ class Main { static function main() { var sample : RestrictAccessSample = new RestrictAccessSample(); trace(sample.publicMethod()); // works and traces “private method” trace(sample.privateMethod());// throws a compilation error } } class RestrictAccessSample { public function new () { } public function publicMethod() : String { return privateMethod(); } private function privateMethod() : String { return “private method”; } } ~~~ 本例中, privateMethod 只能在 SampleClass 类中的上下文中调用,而 publicMethod 总是可以访问,只要是关联到SampleClass 的一个实例。 有 private 的字段是必须的,在不暴露他们的应用程序接口之间传送消息的功能。这个概念在OOP中被称为封装;开发者使用一个类并不需要清楚它的内部构造。 ## 没有参数 假定一个函数参数应该总是提供一个准备好使用的值是错误的。下面例子展示原因: ~~~ class Main { static function main() { var sample = new NullParameterSample(); trace(sample.cube(7)); trace(sample.cube(null)); } } class NullParameterSample { public function new () { } public function cube(n : Float) { return n*n*n; } } ~~~ cube方法接受一个委托的浮点参数。如果测试 参数是 7, 结果是 343,如预期一样。但是使用 null,可以完美运行在除了 Flash 9 以外的平台,但是结果不同。 最好的方式避免这个矛盾,是在使用它们之前验证函数参数,并抛出一个错误(错误处理在第 7 章介绍)或者回退到一个通用的结果。 上面的例子可以通过下面的方法纠正,以适用每个平台: ~~~ public function cube(n : Float) { return if(n == null) null; else n*n*n; } ~~~ ## 可选参数 每个函数参数都可以使用一个可选的 问号 ? 标记前缀。如果存在,表示这个参数是可选的,可以在函数调用时忽略。虽然可选参数在函数声明的任何部分放置,但是通常好的实践是堆在后面。 ~~~ class Main { static function main() { var sample = new OptionalParameterSample(); trace(sample.quote(“quotation”, “ - “)); trace(sample.quote(“quotation”)); } } class OptionalParameterSample { public function new () { } public function quote(text : String, ?quotesSymbol : String) : String { if(quotesSymbol == null) quotesSymbol = ‘”’; return quotesSymbol + text + quotesSymbol; } } ~~~ 第二个参数是可选的, 当函数调用时没有传入第二个参数,它的值会是默认的 null ,实践中,这样写: ~~~ sample.quote(“quotation”); ~~~ 准确的说,应该是下面的写法: ~~~ sample.quote(“quotation”, null); ~~~ 为此,在 quote 函数中, quoteSymbol 的值会被测试,如果是 null ,一个替代默认值被分配。牢记Haxe 中变量的类型,即使是数值类型,也可以假设值 null ;因此经常谨慎的测试一个参数实际上是不是 null,否则可能引起错误。当希望强制某个参数可以假设为一个 null ,最好使其成为可选参数。Flash 9 有一个稍有不同的行为关于读取 null 。12章将会深入介绍。 可选参数是独立的:当多余一个在一行声明的时候,可以为最后的参数设置一个值,而不用为前一个设置,尽管这可能比较混乱。思考下面的例子: ~~~ function foo(?a : String, ?b : Int, ?c : Person) { // body goes here } ~~~ 所有跟随的语句都是正确的,正确的分配到相应的参数;没有显式声明的参数设置为null: ~~~ foo(1); foo(“John”); foo(“John”, 1, person); // where person is an instance of Person foo(person); ~~~ 但是下面的语句不会编译: ~~~ foo(person, “John”); // wrong sequence ~~~ # 变量 * * * * * 类实例变量的声明如下: ~~~ private var name : Type; //访问修饰符可以是 public, private、 和omitted ~~~ 访问修饰符目前和实例函数应用的一致。 和局部变量跟静态变量相对的是,它不能在声明的时候设置实例变量的值;值只能通过内部的函数调用来改变。 实例变量可以在内部函数中引用,只需要使用变量名即可,或者带上前缀 this 。this 标识符是强制在一个函数中使用来应对冲突的名称。 # 标识符优先级 * * * * * 当命名变量和函数时,重要的是意识到如何解析他们的标识符。对于变量和函数分配到变量,优先级优先顺序从高到低,依次是: * 局部变量 * 实例变量 * 静态变量 * 作用于内声明的枚举构造器 函数的优先级如下: * 局部变量中保存的函数 * 实例函数 * 静态函数 如果有多个局部变量或者函数,有相同的名称,最后声明的将被采用。重新声明一个局部变量或者函数并不是一个好的实践。 实例类变量和函数可以来自当前类或者一个父级类,将在继承章节解释。注意函数可以被重载,而变量不行。 s 枚举的作用于是当前的文件或者每个导入的包;这个参数在第6章部分介绍。 最终,可以有一个静态字段是实例字段来分享他们的名字,但是不能使用类变量来通过一个类函数分享名字。 # 构造器 * * * * * 每个类可以被实例化只要它 有一个构造器,这是一个特殊的函数,每次一个新的实例创建时都会被调用。构造器的语法几乎和实例函数的相同,除了它的名字被强制定义为 new 。函数的返回类型总是 Void,可以显示标识或者忽略(omitted)。 因为实例变量不能再声明的位置初始化,构造器可以用于这个目的。 要改进 BlogEntry 类,一个包含常见数据的变量被添加,它的值在实例化时自动生成。 ~~~ class BlogEntry { public var title : String; public var content : String; public var createdOn : Date; public function new () { createdOn = Date.now(); } } ~~~ 构造器,包括其他任何函数,都可以使用参数,在 Blog例子中可以用来设置一些BlogEntry声明的限制,并减少创建一个类可用实例的代码行数。 ~~~ class BlogEntry { public var title : String; public var content : String; public var createdOn : Date; public function new (title : String, content : String) { this.title = title; this.content = content; createdOn = Date.now(); } } ~~~ 实例以这个方式创建: ~~~ class Main { static function main() { var entry : BlogEntry = new BlogEntry(“My First Blog Entry”, “...”); trace(entry.title); } } ~~~ 通常当一个 blog entry被创建,它只是一个草稿,没有真的准备去发布。最好有一个 publish() 方法,在一个请求准备好上线之后来确定状态,和一个 unpublish() 方法,在必要的时候取消发布。 ~~~ class BlogEntry { public var title(default,default) : String; public var content(default,default) : String; public var createdOn : Date; public var onlineInfo(getOnlineInfo,null) : String; private var publishedOn : Date; public function new (title : String, content : String) { this.title = title; this.content = content; createdOn = Date.now(); publishedOn = null; } public function publish() : Void { publishedOn = Date.now(); } public function unpublish() : Void { publishedOn = null; } public function isOnline() : Bool { return publishedOn != null && publishedOn.getTime() < = Date.now().getTime(); } private function getOnlineInfo() : String { if(publishedOn == null) return “Not yet on-line”; else { // getTime() returns the time in milliseconds since 1970-01-01 // we have to divide the time span by 24 hours=24*60*60*1000=86400000 var daysOnline = (Date.now().getTime()-publishedOn.getTime())/86400000; return if(daysOnline < 1) “Published Today”; else if(daysOnline < 2) “Published Yesterday”; else if(daysOnline < 7) “Published “ + Math.floor(daysOnline) + “ days ago”; else “Published on “ + DateTools.format(publishedOn, “%Y-%m-%d”); } } } ~~~ 当私有变量 publishedOn 设置为 null ,记录尚未准备好发布,只是一个草稿,使用了 publish() 方法,设置值为当前的日期,因此,准备好被显示了。方法 isOnline() 用来检查是否记录已经被发布,而 getOnlineInfo() 返回信息,以一个易读的格式,在自发布后到现在消逝的时间。 # 变量修饰符 * * * * * 类变量支持使用修饰符,或者属性,允许修改它们的标准行为: 对变量值的访问可以限制为只读或者只写。 一个 getter 和/或 一个 setter 方法可以声明来访问变量的值。 变量修饰符的一种语法如下: ~~~ private var name(getModifier, setModifier) : Type; ~~~ 访问修饰符和在函数中解释的有相同的行为。type 是属性可以管理的一个类型。 getModifier 和setModifier 可以假设如下描述的一个值: |变量值修饰符|描述| | -- | -- | |null|访问被禁止。用在 getKeyword 位置,它表示变量只允许写;使用它在 setKeyword 表示渲染变量只读。即使没有错误报告,这两个位置用 null 是没有意义的。| |default|变量被视为传统变量。在 getKeyword 和 setKeyword 位置通过 default 声明,和一个实例变量完全相同,但是可以强制语义访问修饰符在未来可能需要改变。| |方法名|指定的方法,通常标记为私有,必须术语和属性的类相同的,用来访问属性值。如果在 getKeyword设置会是一个 getter,否则则是 setter 。| |dynamic|通过一个运行时生成的方法提供访问| 关联了只读/只写 属性的变量在拥有它的类内部具有完全的访问性。只有在外部才具有访问限制,并且只对公共变量有意义。 一个 getter 方法必须符合签名 Void->T, 意味着方法必须接受没有参数并且必须返回一个 T 类型的值,T 是变量的类型。 setter方法的签名是 T->T, 意味着函数必须接收和返回一个和属性本身类型相同的值。 setter 方法,虽然它用来改变一些对象实例内部的内容,必须始终返回一个值(通常连续访问getter方法返回的同样的)。需要的许可语法如下: ~~~ var v = c.step++; ~~~ step是一个通过这种方式定义的属性: ~~~ public var step(getStep,setStep) : Int; private function getStep() : Int { return counter; } private function setStep(value : Int) : Int { counter += 1; return counter; } ~~~ 如果 setStep 不返回一个值, v 会设置为 Void ,这和期待的逻辑相反。 这个修饰符允许限制从类外部对变量的访问,方便的用于 blog entry 的例子来强制 createdOn 变量为只读,所以它的值在创建时被设置,不能在以后被伪造。 ~~~ public var createdOn(default,null) : Date; ~~~ 尝试访问 craetedOn 属性,如果值被读取可以正常执行,但是赋值则会报告一个错误。 ~~~ class Main { static function main() { var entry : BlogEntry = new BlogEntry(“My First Entry”, “...”); trace(entry.createdOn); // works entry.createdOn = Date.now(); // fails } } ~~~ 要展示另一个只读访问的使用,改变例子为介绍一个新的变量 excerpt ,它包括一个小的关于blog entry的描述,使用了 content 属性前面一部分单词。在每次调用时值都被计算,所以类不需要任何支持的变量。 ~~~ public var excerpt(getExcerpt, null) : String; private function getExcerpt() : String { return content.substr(0, 10) + “ ...”; } ~~~ 新的代码可以这样使用: ~~~ class Main { static function main() { var entry : BlogEntry = new BlogEntry( “My First Entry”,“Using the BlogEntry class is easy.”); trace(entry.excerpt); } } ~~~ excerpt 变量现在会输出 content 变量的前十个字符跟随一个空格和一个省略号。某些情况下最好提供一个替代文本,而不是盲目裁剪blog entry 的content。要获得这个,一个 setter 方法和一个新的 private 变量 definedExcerpt 被添加到类。getter 方法也被修改来使用新定义的变量,如果它不同于 null 或者反复用于裁剪功能。 ~~~ private var definedExcerpt : String; public var excerpt(getExcerpt, setExcerpt) : String; private function setExcerpt(value : String) : String { definedExcerpt = value; return definedExcerpt; } private function getExcerpt() : String { return if(definedExcerpt != null) definedExcerpt; else content.substr(0, 10) + “ ...”; } ~~~ excerpt 属性现在可以设置为一个新的值。 ~~~ entry.excerpt = “Blog Entry is easy” ~~~ 在传统的OOP中,getter 和 setter 方法是首选的方式访问变量,但是在 动态语言中,例如 JavaScript,有一个普遍的技巧来忽略这个实践而是使用一个更短的容易的来读代码和更快的执行,通过属性定义你可以总是切换实例变量为访问方法。 dynamic 关键字在变量的上下文中是一个高级特性。要访问一个标记为dynamic的属性的值,必须在运行时提供访问方法。应用本身必须动态提供一个get/set的实现。这可以通过在运行时修改一个实例或者类的属性来实现,使用Haxe的反射机制。 属性通过下面方式定义: ~~~ var runtime(dynamic,dynamic) : String; ~~~ 两种放问题必须被动态提供,必须命名为 get_runtime 和 set_runtime(后缀get/set_ 跟随属性名),如下例子所示: ~~~ Reflect.setField(objectInstance, “get_runtime”, function() { // do something here and return a string return “”; }); Reflect.setField(objectInstance, “set_runtime”, function(value) { // do something here and return a string return value; }); ~~~ 在实践中编译器不会检查get_X 和 set_X的实际存在方法,因此它们可以被提供到运行时。 objectInstance 是一个对象的引用,实现定义runtime属性的类。