和许多面向对象语言类似,类是大多数 Haxe 程序中主要的数据结构。每个 Haxe 类都有一个确定的名字,一个隐含的路径和零或者多个类字段。这里我们将关注类的一般结构及它们之间的关系,而 类字段(第4章) 的详细内容将在之后展开。
以下代码示例作为本节剩余部分的基础:
class Point {
var x : Int;
var y : Int;
public function new(x,y) {
this.x = x;
this.y = y;
}
public function toString() {
return "Point("+x+","+y+")";
}
}
从语义上讲,这个类表示二维空间内的一个点,但是这里它是什么并不重要。我们来描述一下这个结构:
- 关键字
class
表示我们定义一个类 Point
是类的名称,可以使用任何符合类型标识符规则的字符- 包围在花括号
{}
中间的是类的字段 - 它由两个 Int 类型变量字段
x
和y
组成 - 后面是一个特定的函数字段叫做
new
,它是类的构造函数 - 还有一个普通的函数
toString
在 Haxe 中有一个特殊类型,可以兼容所有的类:
类型 :Class 这个类型可以兼容所有类型,也就是说,所有类(而不是它们的实例)可以被分配给它。在编译时,
Class
是所有类的基础类型。然而,这个关系并不会反映在生成的代码中。当一个 API 需要的一个值是一个类而非某个特定的类型时,可以使用这个类型。这应用到 Haxe 反射API (第10.7节) 中的一些方法。
类的实例通过调用类的构造函数(一个通常称为实例化的过程)创建。类实例的另一个称呼叫做对象。然而,我们更倾向于使用术语“类的实例”来强调 类/类实例 及 枚举/枚举实例(第2.4节)之间的类比。
var p = new Point(-1, 65);
这会生成一个 Point
类的实例,它被分配到一个变量 p
上。Point
的构造函数接受两个参数 -1
和 65
,然后分别分配它们到实例变量 x
和 y
(对比在 类实例(第2.3节) 中它的定义)。我们将在 new(第5.12节) 中重新审视 new 表达式的精确意思。现在,只要把它当作调用类的构造函数并返回适当的对象。
类可以继承自其它的类,在 Haxe 中通过 extends 关键字指示:
class Point3 extends Point {
var z : Int;
public function new(x,y,z) {
super(x,y);
this.z = z;
}
}
这个关系通常被称为 “is-a”(subsumption,包含架构,指的是类的父子继承关系):任何 Point3
类的实例同时也是 Point
类的实例。Point
则作为 Point3
的父类,而 Point3
则是 Point
的子类。一个类可以被许多子类继承,但是只能继承一个父类。术语 “某个类的父类” 通常指它的直接父类、父类的父类,以此类推。
上面的代码和原来的 Point
类很相似,使用了两个新的部分:
extends Point
表示这个类继承自 Point
类。
super(x, y)
是调用父类的构造函数,本例中即 Point.new
。
在子类中定义它们自己的构造函数并不是必须的,但是如果定义了,则调用 super()
是必须的。不像其它一些面向对象语言,这个调用可以出现在构造函数代码中的任何地方,而不必作为构造函数中的第一个表达式。
一个类可以重写它父类的 方法(第4.3节),需要显式使用的 override
关键字。效果和限制将在 重写方法(第4.3.1节) 中详细介绍。
一个接口可以被理解为一个类的签名,因为它描述了一个类的公共字段。接口不提供实现,而是单纯地提供结构化信息:
interface Printable {
public function toString():String;
}
语法和类的相似,但有以下例外:
- 使用
interface
关键字而不是class
关键字 - 函数不含有任何表达式
- 每个字段必须有一个显式的类型声明
接口不像 结构化子类型(第3.5.2节),它只描述与类之间的一个静态关系。一个给定的类只有当如下描述时才被认为是与给定接口兼容的:
class Point implements Printable { }
这里,implements
关键字表示 Point
“是一个” Printable
,即每个 Point
的实例同时也是 Printable
接口的实例。虽然一个类只能有一个父类,但是它可以通过使用多个 implements
关键字实现多个接口:
class Point implements Printable
implements Serializable
编译器会确保 implements
的假设成立。也就是说,它会确保类实现了接口所需的所有字段。当一个类或该类的父类中提供了该字段的实现时,该字段才被认为是已实现的。
接口的字段不仅限于方法,也可以是变量或者属性:
interface Placeable {
public var x:Float;
public var y:Float;
}
class Main implements Placeable {
public var x:Float;
public var y:Float;
static public function main() { }
}
接口可以通过使用 extends
关键字继承多个别的接口:
interface Debuggable extends Printable extends Serializable
Haxe 4.0.0 开始:
像类一样,接口也可以通过
final
关键字进行修饰以避免接口被其他接口扩展。
花絮:实现的语法 Haxe 3.0 之前 需要多个
implements
关键字并使用逗号,
进行分隔。我们决定遵循 Java 事实上的标准,免除了逗号。这也是 Haxe 2 和 Haxe 3 之间的一个断层式的变更。