Golang中的interface


在 Go 中,关键字 interface 被赋予了多种不同的含义。每个类型都有接口,意味着对那个类型定义了方法集合 。如下这段代码定义了具有一个字段和两个方法的结构类型 S。

type S struct { i int }
func (p *S) Get() int { return p.i }
func (p *S) Put(v int) { p.i = v }

也可以定义接口类型,仅仅是方法的集合。这里定义了一个有两个方法的接口 I:

type I interface {
Get() int
Put(int) 
}

对于接口 I,S 是合法的实现,因为它定义了 I 所需的两个方法。注意,即便是没有明确定义 S 实现了 I,这也是正确的。
Go 程序可以利用这个特点来实现接口的另一个含义,就是 接口值:

func f(p I) {  //定义一个函数接受一个接口类型作为参数
fmt.Println(p.Get()) //p实现了接口I,必须有get()方法
p.Put(1)  //Put()方法类似
}

这里的变量 p 保存了接口类型的值。因为 S 实现了 I,可以调用 f 向其传递 S 类型的值的指针:

var s S ; f(&s)

获取 s 的地址,而不是 S 的值的原因,是因为在 s 的指针上定义了方法。

在 Go 中的接口有着与许多其他编程语言类似的思路:C++ 中的纯抽象虚基类,Haskell中的 typeclasses 或者 Python 中的 duck typing。然而没有其他任何一个语言联合了接口值、静态类型检查、运行时动态转换,以及无须明确定义类型适配一个接口。这些给 Go 带来的结果是,强大、灵活、高效和容易编写的。

定义另外一个类型同样实现了接口 I:

type R struct { i int }
func (p *R) Get() int { return p.i }
func (p *R) Put(v int) { p.i = v }

函数 f 现在可以接受类型为 R 或 S 的变量。假设需要在函数 f 中知道实际的类型。在Go 中可以使用 type switch 得到。

type switch:

func f(p I) {
switch t := p.(type) { 
case *S: 
case *R: 
case S: 
case R: 
default: 
    } 
}

在 switch 之外使用 (type) 是非法的。类型判断不是唯一的运行时得到类型的方法。
为了在运行时得到类型,同样可以使用 “comma, ok” 来判断一个接口类型是否实现了某个特定接口:

if t, ok := something.(I) ; ok {
// 对于某些实现了接口 I 的
// t 是其所拥有的类型
}

确定一个变量实现了某个接口,可以使用:

t := something.(I)


文章作者: RickDamon
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 RickDamon !
  目录