类(Class)¶
重写成员谓词(Overriding member predicates)
类的定义¶
除了基础的数据类型,你也可以在MirrorQL中定义自己的类型。一种实现方法是定义一个类(Class),类提供了一种简单的方法来重用和构造代码。
例如:
- 将相关联的值分组在一起
- 根据这些值定义成员谓词
- 定义覆盖成员谓词的子类
MirrorQL中的类不会“创建”新对象,而只是表示逻辑属性。如果值满足该逻辑属性,则该值属于特定类。
定义一个类¶
类语法¶
典型的class语法定义如下:
- class的名称 - class的基类列表(即这个class基于哪些类型的交集创建) - class的init谓词(即筛选这个class的逻辑式) - class的成员谓词(可选) - class的成员变量(可选)
class的典型语法如下:
class 类名 extends 基类列表,... {
let member_val1:int;
let member_val2:str; // 成员变量,可选
fn init() {
... // class的init谓词
}
fn member1() -> int{
result == self.member_val1 // self代表class本身
}
fn member2(x:str) {
x == self.member_val2
} // 成员谓词
}
类要素¶
要定义一个类,需要遵循以下的几条规则:
- 关键字
class - 类的名称,这是一个以字母开头的标识符
- 要扩展的类型
- 类的主体,用大括号括起来
例如:
class OneTwoThree extends int {
fn init() {
self.(int) == 1
or self.(int) == 2
or self.(int) == 3
}
fn getAString() -> str {
result == "one, two or three: "
}
}
这里定义一个类OneTwoThree继承自基本的数据类型int,特征谓词是约束变量需要满足值为1、2、3中的一个,MirrorQL并不会创建一个实例对象,而是通过逻辑条件约束变量所满足的条件。
MirrorQL中的类必须始终继承至少一个现有的类,类的值包含在基本类型(int、float、bool、str)的交集内。一个类从其基本类型继承所有的成员谓词。
一个类可以继承自多种类型,如果需要更多的信息,查看多重继承 章节。
一个有效的类必须包含以下的条件:
-
不能继承自这个类本身
-
不能继承不兼容的类型,更多相关信息查看类型兼容章节
类体(Class bodies)¶
类的主体(大括号括起的部分)可以包含:
- 一个特征谓词的声明
- 任意数量的成员谓词声明
- 任意数量的字段声明
当定义一个类时,该类还从其超类型继承所有非私有成员谓词和字段。您可以重写 这些谓词和字段以为其提供更具体的定义。
init谓词(Init predicates)¶
class是从基类的交集中创建的,相当于取子集。init谓词的作用是指定取子集的逻辑式。
用例子说明init谓词的用法。
假如: 要定义一个class SomeNumber,要求每个元素满足特性:元素的值为两个在区间[2,4]内的数的乘积。
首先,class SomeNumber的基类型为int,即从int集合中筛选SomeNumber。再考虑init谓词的写法。
假如只需要实现一个判断是否为SomeNumber的谓词,可以用下面的代码:
init谓词和is_SomeNumber表达的逻辑相同:
init谓词中self表达式的定义类型
在init谓词中,MirrorQL编译器认为self表达式的定义类型为新class(尽管此时新class没有被定义完全)。在实际编程中,应避免在init谓词中使用self的成员谓词,或将self作为新class类型传递给外部谓词。
成员谓词(Member predicates)¶
这些谓词仅适用于特定类别的成员。您可以根据值调用成员谓词。例如,您可以使用上述类中的成员谓词
此调用返回结果。"One, two or three:"
字段(Field)¶
这些是在类主体中声明的变量。一个类在其主体内可以有具体任意数量的字符声明(即变量声明)。
可以在类内部的谓词声明中使用这些变量。就像变量this一样,字段必须限制在特征谓词中。
重写成员谓词(Overriding member predicates)¶
如果类从超类型继承成员谓词,则可以覆盖继承的定义。
为此,您可以定义一个成员谓词,该成员谓词的名称和别名与继承的谓词相同。
如果要优化谓词以为子类中的值提供更具体的结果,这将很有用。
class OneTwoThree extends int {
fn init() {
self.(int) == 1
or self.(int) == 2
or self.(int) == 3
}
}
class OneTwo extends OneTwoThree {
fn init() {
self.(int) == 1
or self.(int) == 2
}
}
class 继承¶
定义一个class时,需要在extends关键词后给出一个基类列表。class SomeNumber的基类列表为int,则称SomeNumber继承int。
之所以称为"继承",是因为新class会继承基类中所有的成员谓词(注意,成员变量没有被继承)。
此外,类型系统中也增加了"新class是基类的子类"这一类型信息。
子类可以重新定义从基类中继承而来的成员谓词,下面的例子说明了重定义基类成员谓词的用法:
class Pet extends str{
fn init(){
self == "pet"
}
fn getName() -> str{
result == "some pet"
}
}
class Dog extends Pet{
// init谓词缺省为any()
fn getName() -> str{ // 重写从Pet中继承的成员谓词
result == "dog"
}
}
fn test(){
let x:Pet;
x.getName() == "some pet" and x.(Dog).getName() == "dog" and test2(x.(Dog).(Pet))
}
fn test2(x:Pet){
x.getName() == "some pet" // 成员谓词的解析只和表达式的定义类型有关
}
多重继承¶
一个class可以同时从多个基类中继承,相当于从多个类中取交集。
例如extends SmallInt,SomeNumber表示,为init谓词选定的初始集合为SmallInt和SomeNumber的交集。
和单继承类似,新class将继承每个基类的成员谓词,但如果某些基类存在名称相同但定义不同的成员谓词,则继承时会出错,需要手动重写谓词,或使用具名继承来避免歧义。
下面的例子展示了多继承的用法:
class SmallInt extends int{
fn init(){
...
}
fn getName() -> str{
result == "small int"
}
}
class SomeNumber extends int{
fn init(){
...
}
fn getName() -> str{
result == "some number"
}
}
class NewClass extends SmallInt,SomeNumber {
fn init(){
...
}
fn getName() -> str{ // 此处需要重新定义getName,否则会存在getName的二义性
result == "new class"
}
}
名称相同但相同的成员谓词
在复杂的继承关系中,可能存在菱形继承的情况,此时在某个新class中可能存在名称相同、定义相同,但从两个不同基类继承而来的成员谓词。此时我们认为不存在二义性,不必重新定义成员谓词。
下面的代码演示了菱形继承的情况:
class继承和多继承的要求
class继承如果存在环,则是非法的,例如:A extends B; B extends A非法。此外,某个class的基类列表中的各个类型必须兼容,extends str,int是非法的。