扩展
扩展可以为除函数、元组、接口外的在当前 package 可见的任何类型添加新功能。
可以添加的功能包括:
- 添加实例成员函数
- 添加静态成员函数
- 添加操作符重载
- 添加实例成员属性
- 添加静态成员属性
- 实现接口
扩展是在类型定义之后追加的功能,扩展不能破坏原有类型的封装性,因此以下功能是禁止的。
- 扩展不能增加字段。
- 扩展不能增加抽象成员。
- 扩展不能增加
open
成员。 - 扩展不能
override/redef
原有的成员。 - 扩展不能访问原类型的私有成员。
扩展语法
一个简单的扩展示例如下:
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
禁止定义孤儿扩展,孤儿扩展指的是接口扩展既不与接口(包含接口继承链上的所有接口)定义在同一个包中,也不与被扩展类型定义在同一个包中。
即接口扩展只允许以下两种情况:
- 接口扩展与类型定义处在同一个包。
- 接口扩展实现的接口,和接口的继承链上所有未被被扩展类型实现的接口,都必须在同一个包中。
其它情况的接口扩展都是不允许定义的。
// 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) {}
}
修饰符
扩展内的函数定义支持使用 private
,protected
(仅限于被扩展类型是 class 类型)或 public
修饰。
使用 private
修饰的函数只能在本扩展内使用,外部不可见。
使用 protected
修饰的成员函数除了能在本包内被访问,对包外的当前 class 子类也可以访问。
没有使用 private
,protected
或 public
修饰的函数只能在本包内使用。
// 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
}
}
扩展内的函数定义不支持使用 open
、override
、redef
修饰。
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) {}
}
}
修饰符
扩展内的属性定义支持使用 private
,protected
(仅限于被扩展类型是 class 类型)或 public
修饰。
使用 private
修饰的属性只能在本扩展内使用,外部不可见。
使用 protected
修饰的属性除了能在本包内被访问,对包外的当前 class 子类也可以访问。
没有使用 private
,protected
或 public
修饰的属性只能在本包内使用。
// 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 }
}
}
扩展内的属性定义不支持使用 open
、override
、redef
修饰。
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
}
扩展的访问和遮盖
扩展的实例成员与类型定义处一样可以使用 this
,this
的含义与类型定义中的保持一致。同样也可以省略 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
和扩展 2
,X
的约束在扩展 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