企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
一些类或接口是特定操作其他类的实例。这些操作可以被在不分享任何通用父类或接口类的实例执行。考虑如下例子: ~~~ class BinaryNode { private var content : String; public var parent : BinaryNode; public var leftChild : BinaryNode; public var rightChild : BinaryNode; public function new (content : String) { this.content = content; } public function getContent() : String { return content; } } ~~~ BinaryNode 类是一个非常简单的容器来层级结构存储信息。这个例子,每个节点的内容是String类型,但是它听起来很显然,完全相同的 node 类可以简单的被改变来存储任何类型的值。要这样做使用至今获得的只是,问题可以以两种方式之一解决:声明一个新的 node 类对每个存储到它的类型或者使用一个 Dynamic类型的内容。第一种方案非常重复(记住DRY原则,don't repeat yourself),而且当实现只有一点改变的时候非常痛苦。第二个方案是脆弱的因为它需要不断地投射到一个合适的类型每次 getContent() 被调用时,并且总是一个好的实践来避免不安全的投射如果不是绝对必要。 需要的是一个通用的方法(generics是用在C#和JAVA等语言中同样上下文的术语);这完全就是所用的类型参数的目的。一个类型参数是一个通用的具体类型的占位符。 重新编码,按照如下: ~~~ class BinaryNode < T > { private var content : T; public var parent : BinaryNode < T > ; public var leftChild : BinaryNode < T > ; public var rightChild : BinaryNode < T > ; public function new (content : T) { this.content = content; } public function getContent() : T { return content; } } ~~~ 在类的实例化中一个类型必须提供 T 按照如下方式: ~~~ var ns : BinaryNode < String > = new BinaryNode < String > (“I’m a node”); ~~~ 另一个例子是: ~~~ var ni : BinaryNode < Int > = new BinaryNode < Int > (7); ~~~ 现在 ns.getContent() 会返回一个String类型的值,而ni.getContent() 会返回一个Int 类型的值。 你可以缩短类型声明,通过类型推断来减少代码: ~~~ var ns = new BinaryNode(“I’m a node”); ~~~ String类型参数可以被从构造器忽略,因为编译工具可以推断出正确的类型,从文本参数。变量不需要任何类型声明,因为完整类型可以通过构造器推断出来。 对象创建时的类型声明总是保留不变直到对象呗垃圾回收,在运行时不可改变。 给类参数名字 T 只是一个惯例。多于一个的类参数可以通过一个逗号分隔来指定,在括号中间。 类型参数也可以在函数层被指定;这个情况generic参数有一个目的只有在声明函数的上下文,而且和类的类型参数声明相同的方式,但是位于函数名后面。 ~~~ public static function indexOf < T > (v : T, a : Array < T > ) { for(i in 0...a.length) if(v == a[i]) return i; return -1; } ~~~ 这个函数有一个值类型T的参数,和一个相同类型数组,返回array中值得位置,如果没有找到返回-1 。这个函数被以传统方式使用: ~~~ var l = [1,2,4,8,16]; // l is of type Array < Int > trace(indexOf(8, l)); ~~~ 这会输出3或者带有另一个类型的数组: ~~~ var ls = [“A”,”B”,”C”,”D”]; // ls is of type Array< String > trace(indexOf(“C”, ls)); ~~~ # 类型参数限制 * * * * * 通用的是有用的,但是太通用可能造成混淆。鉴于此,Haxe提供一个方式来限制类型参数只能使用一些类型。限制参数使用如下的语法: ~~~ class Name<T : Type, T : (Type, Type)> ~~~ 圆括号的使用是强制消除歧义,当多于1个类参数使用的时候。多个约束通常是一个类和一个或者多个接口的结合;类型 T 必须满足所有列出的约束。类型约束不局限于类,还可以应用到接口中,enum,和 typedef 。 当使用类型约束,可以实现特定的对象容器,如下: ~~~ class Item { public function new() {} } class Movie extends Item { } class Butterly extends Item { } class Collection < T : Item > { public function new() {} public function add(item : T) { // implementation goes here } } ~~~ 在你的应用中你可以使用 Collection 类型来创建一个容器对于电影或者蝴蝶。 ~~~ var movies = new Collection < Movie > (); // the constraint is on the class Movie movies.add(new Movie()); // accepted value movies.add(new Butterly()); // compiler does not permit this ~~~ 约束也可以使用标准类型。一个约束Float类型可以限制数值的访问;在实例化中可以标记状态如果值必须是整数或实数。 ~~~ class Point < T : Float > { public var x : T; public var y : T; public function new(x : T, y : T) { this.x = x; this.y = y; } } ~~~ 这个类可以像如下使用: ~~~ var pInt = new Point < Int > (10, 20); // pInt.x = 0.1; // does not compile var pFloat = new Point < Float > (0.1, 0.2); ~~~