49-拓展(1)
49-拓展(1)
扩展概述
扩展可以为在当前 package
可见的类型(除函数、元组、接口)添加新功能。
当不能破坏被扩展类型的封装性,但希望添加额外的功能时,可以使用扩展。
可以添加的功能包括:
- 添加成员函数
- 添加操作符重载函数
- 添加成员属性
- 实现接口
扩展虽然可以添加额外的功能,但不能变更被扩展类型的封装性,因此扩展不支持以下功能:
- 扩展不能增加成员变量。
- 扩展的函数和属性必须拥有实现。
- 扩展的函数和属性不能使用
open
、override
、redef
修饰。 - 扩展不能访问被扩展类型中
private
修饰的成员。
根据扩展有没有实现新的接口,扩展可以分为 直接扩展 和 接口扩展 两种用法,直接扩展即不包含额外接口的扩展;接口扩展即包含接口的扩展,接口扩展可以用来为现有的类型添加新功能并实现接口,增强抽象灵活性。
直接拓展
对类型直接进行拓展称为直接拓展。
简单拓展
这里演示一个比较简单的拓展类型的用法。 给String类型拓展一个printSize的方法。
需要注意的是,String类型本身具有size属性,所以 this.size
不会出错
extend String {
public func printSize() {
println("the size is ${this.size}")
}
}
main() {
let a = "123"
a.printSize() // the size is 3
}
泛型拓展
当被拓展的类型是泛型时,提供了两种方式对泛型类型进行拓展。
- 针对特定泛型实例化类型进行扩展
extend
后面引入泛型形参的泛型扩展
两种区别主要在写法不同上
针对特定泛型实例化类型进行扩展
针对特定泛型实例化类型进行扩展,关键字 extend
后允许带一个任意实例化完全的泛型类型。为这些类型增加的功能只有在类型完全匹配时才能使用,且泛型类型的类型实参必须符合泛型类型定义处的约束要求。
如下列代码, A是B的父类。B必定包含A所有的实现,所以where的约束生效。
open class A {
func a() {
return true
}
}
class B <: A {
func b() {
return true
}
}
class Foo<T> where T <: A {}
extend Foo<B> {}
main() {
var a = Foo<B>()
}
extend 后面引入泛型形参的泛型扩展
在 extend
后面引入泛型形参的泛型扩展。泛型扩展可以用来扩展未实例化或未完全实例化的泛型类型。在 extend
后声明的泛
型形参必须被直接或间接使用在被扩展的泛型类型上。为这些类型增加的功能只有在类型和约束完全匹配时才能使用。
class MyList<T> {
public let data: Array<T> = Array<T>()
}
extend<T> MyList<T> {} // OK
extend<R> MyList<R> {} // OK
extend<T, R> MyList<(T, R)> {} // OK
例如可以定义一个叫 Pair 的类型,这个类型可以方便地存储两个元素(类似于 Tuple)。
希望 Pair 类型可以容纳任何类型,因此两个泛型变元不应该有任何约束,这样才能保证 Pair 能容纳所有类型。
但同时又希望当两个元素可以判等的时候,让 Pair 也可以判等,这时就可以用扩展来实现这个功能。
如下面的代码所示,使用扩展语法,约束了 T1 和 T2 在支持 equals 的情况下,Pair 也可以实现 equals 函数。
class Pair<T1, T2> {
var first: T1
var second: T2
public init(a: T1, b: T2) {
first = a
second = b
}
}
interface Eq<T> {
func equals(other: T): Bool
}
extend<T1, T2> Pair<T1, T2> where T1 <: Eq<T1>, T2 <: Eq<T2> {
public func equals(other: Pair<T1, T2>) {
first.equals(other.first) && second.equals(other.second)
}
}
class Foo <: Eq<Foo> {
public func equals(other: Foo): Bool {
true
}
}
main() {
let a = Pair(Foo(), Foo())
let b = Pair(Foo(), Foo())
println(a.equals(b)) // true
}
接口扩展
拓展泛型的同时一并实现泛型,称为接口拓展。
interface PrintSizeable {
func printSize(): Unit
}
extend<T> Array<T> <: PrintSizeable {
public func printSize() {
println("The size is ${this.size}")
}
}
当使用扩展为 Array
实现 PrintSizeable
之后,就相当于在 Array
定义时实现接口 PrintSizeable
。
因此可以将 Array
作为 PrintSizeable
的实现类型来使用了,如以下代码所示。
main() {
let a: PrintSizeable = Array<Int64>()
a.printSize() // 0
}
可以在同一个扩展内同时实现多个接口,多个接口之间使用 &
分开,接口的顺序没有先后关系。
如下面代码所示,可以在扩展中为 Foo
同时实现 I1
、I2
、I3
。
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(): Unit {}
public func f2(): Unit {}
public func f3(): Unit {}
}
也可以在接口扩展中声明额外的泛型约束,来实现一些特定约束下才能满足的接口。
例如可以让上面的 Pair
类型实现 Eq
接口,这样 Pair
自己也能成为一个符合 Eq
约束的类型,如下代码所示。
class Pair<T1, T2> {
var first: T1
var second: T2
public init(a: T1, b: T2) {
first = a
second = b
}
}
interface Eq<T> {
func equals(other: T): Bool
}
extend<T1, T2> Pair<T1, T2> <: Eq<Pair<T1, T2>> where T1 <: Eq<T1>, T2 <: Eq<T2> {
public func equals(other: Pair<T1, T2>) {
first.equals(other.first) && second.equals(other.second)
}
}
class Foo <: Eq<Foo> {
public func equals(other: Foo): Bool {
true
}
}
main() {
let a = Pair(Foo(), Foo())
let b = Pair(Foo(), Foo())
println(a.equals(b)) // true
}