扩展

扩展可以为除函数、元组、接口外的在当前 package 可见的任何类型添加新功能。

可以添加的功能包括:

  • 添加实例成员函数
  • 添加静态成员函数
  • 添加操作符重载
  • 添加实例成员属性
  • 添加静态成员属性
  • 实现接口

扩展是在类型定义之后追加的功能,扩展不能破坏原有类型的封装性,因此以下功能是禁止的。

  1. 扩展不能增加字段。
  2. 扩展不能增加抽象成员。
  3. 扩展不能增加 open 成员。
  4. 扩展不能 override/redef 原有的成员。
  5. 扩展不能访问原类型的私有成员。

扩展语法

一个简单的扩展示例如下:

extend String {
    func printSize() {
        print(this.size)
    }
}

"123".printSize() // 3

扩展定义的语法如下:

extendDefinition
    : 'extend' extendType
      ('<:' superInterfaces)? genericConstraints?
      extendBody
    ;

extendType
    : (typeParameters)? (identifier NL* DOT  NL*)* identifier (NL* typeArguments)?
    | INT8
    | INT16
    | INT32
    | INT64
    | INTNATIVE
    | UINT8
    | UINT16
    | UINT32
    | UINT64
    | UINTNATIVE
    | FLOAT16
    | FLOAT32
    | FLOAT64
    | CHAR
    | BOOLEAN
    | NOTHING
    | UNIT
    ;

extendBody
    : '{' extendMemberDeclaration* '}'
    ;
    
extendMemberDeclaration
    : (functionDefinition
    | operatorFunctionDefinition
    | propertyDefinition
    | macroExpression
    ) end*
    ;

扩展的定义使用 extend 关键字,扩展定义依次为 extend 关键字、可选的泛型形参、被扩展的类型、可选的实现接口、可选的泛型约束,以及扩展体的定义。扩展体的定义不允许省略 {}

扩展只能定义在 top level。

扩展分为直接扩展和接口扩展两种用法,直接扩展不需要声明额外的接口。

直接扩展

直接扩展不需要声明额外的接口,可以用来直接为现有的类型添加新功能。

class Foo {}

extend Foo {
    func f() {}
}

main() {
    let a = Foo()
    a.f() // call extension function
}

接口扩展

接口扩展可以用来为现有的类型添加新功能并实现接口,增强抽象灵活性。使用接口扩展时必须要声明被实现的接口。

interface I {
    func f(): Unit
}

class Foo {}

extend Foo <: I {
    public func f() {}
}

对一个类型使用接口扩展功能实现接口 I,其等价于在类型定义时实现接口 I,但使用范围会受到扩展导入导出的限制,详细见扩展的导入导出。

func g(i: I) {
    i.f()
}

main() {
    let a = Foo()
    g(a)
}

我们可以在同一个扩展内同时实现多个接口,多个接口之间使用 & 分开,接口的顺序没有先后关系。

interface I1 {
    func f1(): Unit
}

interface I2 {
    func f2(): Unit
}

interface I3 {
    func f3(): Unit
}

class Foo {}

extend Foo <: I1 & I2 & I3 {
    public func f1() {}
    public func f2() {}
    public func f3() {}
}

如果被扩展类型已经实现过某个接口,则不能通过扩展重复实现该接口,包含使用扩展实现过的接口。

interface I {
    func f(): Unit
}
 
class Foo <: I {
    func f(): Unit {}
}
extend Foo <: I {} // error, can not repeat the implementation of the interface
 
class Bar {}
extend Bar <: I {
    func f(): Unit {}
}
extend Bar <: I {} // error, already implemented through the extension can not repeat the implementation

如果被扩展类型已经直接实现某个非泛型接口,则不能使用扩展重新实现该接口;如果被扩展类型已经直接实现某个泛型接口,则不能使用相同的类型参数扩展重新实现该接口。

interface I1 {}

class Foo <: I1 {}

extend Foo <: I1 {} // error

interface I2<T> {}

class Goo<T> <: I2<Int32> {}

extend Goo<T> <: I2<Int32> {} // error

extend Goo<T> <: I2<T> {} // ok

如果被扩展的类型已经包含接口要求的函数,则接口扩展不能再重新实现这些函数,也不会再使用接口中的默认实现。

class Foo {
    public func f() {}
}

interface I {
    func f(): Unit
}

extend Foo <: I {} // ok

extend Foo {
    public func g(): Unit {
        print("In extend!")
    }
}

interface I2 {
    func g(): Unit {
        print("In interface!")
    }
}

extend Foo <: I2 {} // ok, default implementation of g in I2 is no longer used

禁止定义孤儿扩展,孤儿扩展指的是接口扩展既不与接口(包含接口继承链上的所有接口)定义在同一个包中,也不与被扩展类型定义在同一个包中。

即接口扩展只允许以下两种情况:

  1. 接口扩展与类型定义处在同一个包。
  2. 接口扩展实现的接口,和接口的继承链上所有未被被扩展类型实现的接口,都必须在同一个包中。

其它情况的接口扩展都是不允许定义的。

// package pkg1
public class Foo {}
public class Goo {}

// package pkg2
public interface Bar {}
extend Goo <: Bar {}

// package pkg3
import pkg1.Foo
import pkg2.Bar

extend Foo <: Bar {} // error

interface Sub <: Bar {}

extend Foo <: Sub {} // error

extend Goo <: Sub {} // ok, 'Goo' has implemented the interface 'Bar' on the inheritance chain in pkg2.

扩展的成员

扩展的成员包括:静态成员函数、实例成员函数、静态成员属性、实例成员属性、操作符重载函数。

函数

扩展可以对被扩展类型添加函数,这些函数可以是泛型函数,支持泛型约束,支持重载,也支持默认参数和命名参数。这些函数都不能是抽象函数。

例如:

interface I {}
extend Int64 {
    func f<T>(a: T, b!: Int64 = 0) where T <: I {}
    func f(a: String, b: String) {}
}

修饰符

扩展内的函数定义支持使用 privateprotected(仅限于被扩展类型是 class 类型)或 public 修饰。

使用 private 修饰的函数只能在本扩展内使用,外部不可见。

使用 protected 修饰的成员函数除了能在本包内被访问,对包外的当前 class 子类也可以访问。

没有使用 privateprotectedpublic 修饰的函数只能在本包内使用。

// file1 in package p1
package p1

public open class Foo {}

extend Foo {
    private func f1() {}   // ok
    public func f2() {}    // ok
    protected func f3() {} // ok
    func f4() {}           // visible in the package
}

main() {
    let a = Foo()
    a.f1() // error, can not access private function
    a.f2() // ok
    a.f3() // ok
    a.f4() // ok
}

// file2 in package p2
package p2

import p1.*

class Bar <: Foo {
    func f() {
        f1() // error, can not access private function
        f2() // ok
        f3() // ok
        f4() // error, can not access default function
    }
}

扩展内的函数支持使用 static 修饰。

class Foo {}

extend Foo {
    static func f() {}
}

对 struct 类型的扩展可以定义 mut 函数。

struct Foo {
    var i = 0
}

extend Foo {
    mut func f() { // ok
        i += 1
    }
}

扩展内的函数定义不支持使用 openoverrideredef 修饰。

class Foo {
    public open func f() {} 
    static func h() {}
}

extend Foo {
    public override func f() {} // error
    public open func g() {} // error
    redef static func h() {} // error
}

属性

扩展可以对被扩展类型添加属性。这些属性都不能是抽象属性。

例如:

extend Int64 {
    mut prop i: Int64 {
        get() {
            0
        }
        set(value) {}
    }
}

修饰符

扩展内的属性定义支持使用 privateprotected(仅限于被扩展类型是 class 类型)或 public 修饰。

使用 private 修饰的属性只能在本扩展内使用,外部不可见。

使用 protected 修饰的属性除了能在本包内被访问,对包外的当前 class 子类也可以访问。

没有使用 privateprotectedpublic 修饰的属性只能在本包内使用。

// file1 in package p1
package p1

public open class Foo {}

extend Foo {
    private prop v1: Int64 {   // ok
        get() { 0 }
    }
    public prop v2: Int64 {    // ok
        get() { 0 }
    }
    protected prop v3: Int64 { // ok
        get() { 0 }
    } 
    prop v4: Int64 {           // visible in the package
        get() { 0 }
    }
}

main() {
    let a = Foo()
    a.v1 // error, can not access private property
    a.v2 // ok
    a.v3 // ok
    a.v4 // ok
}

// file2 in package p2
package p2

import p1.*

class Bar <: Foo {
    func f() {
        v1 // error, can not access private function
        v2 // ok
        v3 // ok
        v4 // error, can not access default function
    }
}

扩展内的属性支持使用 static 修饰。

class Foo {}

extend Foo {
    static prop i: Int64 {
        get() { 0 }
    }
}

扩展内的属性定义不支持使用 openoverrideredef 修饰。

class Foo {
    open prop v1: Int64 {
        get() { 0 }
    }
    static prop v2: Int64 {
        get() { 0 }
    }
}

extend Foo {
    override prop v1: Int64 { // error
        get() { 0 }
    }
    open prop v3: Int64 { // error
        get() { 0 }
    }
    redef static prop v2: Int64 { // error
        get() { 0 }
    }
}

泛型扩展

如果被扩展的类型是泛型类型,有两种扩展语法可以对泛型类型扩展功能。

一种是上面介绍的非泛型扩展。对于泛型类型,非泛型扩展可以针对特定的泛型实例化类型进行扩展。

在 extend 后允许是一个任意实例化完全的泛型类型。为这些类型增加的功能只有在类型完全匹配时才能使用。

extend Array<String> {} // ok
extend Array<Int> {} // ok

在扩展中,泛型类型的类型实参必须符合泛型类型定义处的约束要求,否则会编译报错。

class Foo<T> where T <: ToString {}

extend Foo<Int64> {} // ok

class Bar {}
extend Foo<Bar> {} // error

另一种是在 extend 后面引入泛型形参的泛型扩展。泛型扩展可以用来扩展未实例化或未完全实例化的泛型类型。

在 extend 后允许声明泛型形参,这些泛型形参必须被直接或间接使用在被扩展的泛型类型上。为这些类型增加的功能只有在类型和约束完全匹配时才能使用。

extend<T> Array<T> {} // ok

泛型扩展引入的泛型变元必须在被扩展类型中使用,否则报未使用错误。

extend<T> Array<T> {} // ok
extend<T> Array<Option<T>> {} // ok
extend<T> Array<Int64> {} // error
extend<T> Int64 <: Equatable<T> { // error
    ...
}

不管是泛型扩展还是非泛型扩展,对泛型类型扩展都不能重定义成员和重复实现接口。

当泛型扩展的泛型变元被直接使用在被扩展类型的类型实参时,对应的泛型变元会隐式引入被扩展类型定义时的泛型约束。

class Foo<T> where T <: ToString {}

extend<T> Foo<T> {} // T <: ToString

泛型扩展引入的泛型形参不能用于被扩展类型或被实现的接口,否则会编译报错。

extend<T> T {} // error
extend<T> Unit <: T {} // error

我们可以在泛型扩展中使用额外的泛型约束。通过这种方式添加的成员或接口,只有当该类型的实例在满足扩展的泛型约束时才可以使用,否则会报错。

class Foo<T> {
    var item: T
    init(it: T) {
        item = it
    }
}

interface Eq<T> {
    func equals(other: T): Bool
}

extend<T> Foo<T> <: Eq<Foo<T>> where T <: Eq<T> {
    public func equals(other: Foo<T>) {
        item.equals(other.item)
    }
}

class A {}
class B <: Eq<B> {
    public func equals(other: B) { true }
}

main() {
    let a = Foo(A())
    a.equals(a) // error, A has not implement Eq
    let b = Foo(B())
    b.equals(b) // ok, B has implement Eq
}

泛型类型不能重复实现同一接口。对于同一个泛型类型,无论它是否完全实例化,都不能重复实现同一接口。如果重复实现会在编译时报错。

interface I {}
extend Array<String> <: I {} // error, can not repeatedly implement the same interface
extend<T> Array<T> <: I {} // error, can not repeatedly implement the same interface

interface Bar<T> {}
extend Array<String> <: Bar<String> {} // error, can not repeatedly implement the same interface
extend<T> Array<T> <: Bar<T> {} // error, can not repeatedly implement the same interface

对于同一个泛型类型,无论它是否完全实例化,都不能定义相同类型或相同签名的成员。如果重复定义会在编译时报错。

// case 1
extend Array<String> {
    func f() {} // error, cannot be repeatedly defined
}
extend<T> Array<T> {
    func f() {} // error, cannot be repeatedly defined
}
// case 2
extend Array<String> {
    func g(a: String) {} // error, cannot be repeatedly defined
}
extend<T> Array<T> {
    func g(a: T) {} // error, cannot be repeatedly defined
}
// case 3
extend<T> Array<T> where T <: Int {
    func g(a: T) {} // error, cannot be repeatedly defined
}
extend<V> Array<V> where V <: String {
    func g(a: V) {} // error, cannot be repeatedly defined
}
// case 4
extend Array<Int>  {
    func g(a: Int) {} // ok
}
extend Array<String>  {
    func g(a: String) {} //ok
}

扩展的访问和遮盖

扩展的实例成员与类型定义处一样可以使用 thisthis 的含义与类型定义中的保持一致。同样也可以省略 this 访问成员。

扩展的实例成员不能使用 super

class A {
    var v = 0
}

extend A {
    func f() {
        print(this.v) // ok
        print(v) // ok
    }
}

扩展不能访问被扩展类型的 private 成员,其它修饰符修饰的成员遵循可见性原则。

class A {
    private var v1 = 0
    protected var v2 = 0
}

extend A {
    func f() {
        print(v1) // error
        print(v2) // ok
    }
}

扩展不允许遮盖被扩展类型的任何成员。

class A {
    func f() {}
}

extend A {
    func f() {} // error
}

扩展也不允许遮盖被扩展类型的其它扩展中已增加的任何成员。

class A {}

extend A {
    func f() {}
}

extend A {
    func f() {} // error
}

在同一个 package 内对同一类型可以扩展任意多次。

在扩展中可以直接使用(不加任何前缀修饰)其它对同一类型的扩展中的非 private 修饰的成员。

class Foo {}

extend Foo { // OK
    private func f() {}
    func g() {}
}

extend Foo { // OK
    func h() {
        g() // OK
        f() // Error
    }
}

扩展泛型类型时,可以使用额外的泛型约束。

泛型类型的扩展中能否直接使用其它对同一类型的扩展中的成员,除了满足上述可访问性规则之外,还需要满足以下约束规则:

  • 如果两个扩展的约束相同,则两个扩展中可以直接使用对方的成员;
  • 如果两个扩展的约束不同,且两个扩展的约束有包含关系,约束更严格的扩展中可以直接使用约束更宽松的扩展中的成员,反之,则不可直接使用;
  • 当两个扩展的约束不同时,且两个约束不存在包含关系,则两个扩展中不可以直接使用对方的成员。

示例:假设对同一个类型 E<X> 的两个扩展分别为扩展 1 和扩展 2X 的约束在扩展 1 中比扩展 2 中更严格,那扩展 1 中可直接使用扩展2中的成员,反之,扩展 2 中不可直接使用扩展 1的成员。

// B <: A
class E<X> {}

interface I1 {
    func f1(): Unit
}
interface I2 {
    func f2(): Unit
}

extend<X> E<X> <: I1 where X <: B {  // extension 1
    public func f1(): Unit {
        f2() // OK
    }
}

extend<X> E<X> <: I2 where X <: A   { // extension 2
    public func f2(): Unit {
        f1() // Error
    }
}

扩展的继承

如果被扩展的类型是 class,那么扩展的成员会被子类继承。

open class A {}

extend A {
    func f() {}
}

class B <: A {
    func g() {
        f() // ok
    }
}

main() {
    let x = B()
    x.f() // ok
}

需要注意的是,如果在父类中扩展了成员,由于继承规则限制,子类中就无法再定义同名成员,同时也无法覆盖或重新实现(允许函数重载)。

open class A {}

extend A {
    func f() {}
    func g() {}
}

class B <: A {
    func f() {} // error
    override func g() {} // error
}

如果在同包内对同一个类型分别扩展实现父接口和子接口,那么编译器会先检查实现父接口的扩展,然后再检查实现子接口的扩展。

class Foo {}

interface I1 {
   func f() {}
}
interface I2 <: I1 {
   func g() {}
}

extend Foo <: I1 {} // first check
extend Foo <: I2 {} // second check

扩展的导入导出

extend 前不允许有修饰符,扩展只能与被扩展类型或接口一起导入导出。

直接扩展的导出

当直接扩展与被扩展类型的定义处在相同的 package 时,如果被扩展类型是导出的,扩展会与被扩展类型一起被导出,否则扩展不会被导出。

package pkg1

public class Foo {}

extend Foo {
    public func f() {}
}

///////

package pkg2
import pkg1.*

main() {
    let a = Foo()
    a.f() // ok
}

当直接扩展与被扩展类型的定义处在不同的 package 时,扩展永远不会被导出,只能在当前 package 使用。

package pkg1
public class Foo {}

///////

package pkg2
import pkg1.*

extend Foo {
    public func f() {}
}

func g() {
    let a = Foo()
    a.f() // ok
}

///////

package pkg3
import pkg1.*
import pkg2.*

main() {
    let a = Foo()
    a.f() // error
}

接口扩展的导出

当接口扩展与被扩展类型在相同的包时,对于外部可见类型只可以扩展同样外部可见的接口。

package pkg1
public class Foo {}

interface I1 {
    func f(): Unit
}

extend Foo <: I1 { // error
    public func f(): Unit {}
}

public interface I2 {
    func g(): Unit
}

extend Foo <: I2 { // ok
    public func g(): Unit {}
}

///////

package pkg2
import pkg1.*

main() {
    let a = Foo()
    a.g() // ok
}

当接口扩展与被扩展类型不在相同的包时,扩展的访问等级与相应接口相同。如果扩展多个接口,扩展的访问等级是 public 当且仅当所有接口都是 public 的。

package pkg1
public class Foo {}

public class Bar {}

///////

package pkg2

import pkg1.*

interface I1 {
    func f(): Unit
}

public interface I2 {
    func g(): Unit
}

extend Foo <: I1 & I2 { // not external
    public func f(): Unit {}
    public func g(): Unit {}
}

extend Bar <: I2 { // external
    public func g(): Unit {}
}

导入扩展

扩展会与被扩展的类型和扩展实现的接口一起被导入,不能指定导入扩展。

package pkg1
public class Foo {}

extend Foo {
    public func f() {}
}

///////

package pkg2
import pkg1.Foo // import Foo

main() {
    let a = Foo()
    a.f() // ok
}

特别的,由于扩展不支持遮盖任何被扩展类型的成员,当导入的扩展发生重定义时将会报错。

package pkg1
public class Foo {}

///////

package pkg2
import pkg1.Foo

public interface I1 {
    func f(): Unit {}
}

extend Foo <: I1 {} // ok, external

///////

package pkg3
import pkg1.Foo

public interface I2 {
    func f(): Unit {}
}

extend Foo <: I2 {} // ok, external

///////

package pkg4
import pkg1.Foo
import pkg2.I1 // error
import pkg3.I2 // error