Go 接口(第三部分)

xmge · 2018-11-10 09:48:14 · 1278 次点击 · 预计阅读时间 5 分钟 · 大约8小时之前 开始浏览    
这是一个创建于 2018-11-10 09:48:14 的文章,其中的信息可能已经有所发展或是发生改变。

本文介绍的是 Golang 接口主题的另一部分。主要内容包括接口中的方法,接口类型的值作为 map 中的 key,或者作为内置字段。

方法和接口

Go 是有方法的概念的。可以通过调用类型 T 中的方法来获得一个函数,此函数可以从类型 T 中额外地获得明确的参数。

type T struct {
    name string
}

func (t *T) SayHi() {
    fmt.Printf("Hi, my name is %s\n", t.name)
}

func main() {
    t := &T{"foo"}
    f := (*T).SayHi
    f(t) // Hi, my name is foo
}

Golang 语言规范同样允许接口类型使用方法:

在一个接口类型中,通过方法来获取函数是符合规范的。从中获得的函数拥有一个明确的接受者。让我们来看一个例子:

type I interface {
    M(name string)
}
type T struct {
    name string
}
func (t *T) M(name string) {
    t.name = name
}
func main() {
    f := I.M
    var i I = &T{"foo"}
    f(i, "bar")
    fmt.Println(i.(*T).name) // bar
}

接口作为接收者

Go 允许定义方法 —— 接受了特定类型的函数:

type T struct {
    name string
}
func (t *T) SayHi() {
    fmt.Printf("Hi, my name is %s\n", t.name)
}
func main() {
    t1 := T{"foo"}
    t1.SayHi() // Hi, my name is foo
    t2 := &T{"bar"}
    t2.SayHi() // Hi, my name is bar
}

方法添加了一个接受类型(上面的代码片段的接受类型是 *T)。这个方法可以被类型为 *T 或者 T 的调用。在这里顺便提一下,接口类型是不可以作为函数的接受者的:

type I interface {}
func (I) M() {}

这段代码将抛出一个编译期错误 invalid receiver type I (I is an interface type)。在第一部分和第二部分中有更多的方法进行介绍。

接口中“继承 "

结构体的内嵌字段使其实现了接口的方法,于是这个这个结构体继承这个接口

type T1 struct {
    field1 string
}
func (t *T1) M() {
    t.field1 = t.field1 + t.field1
}
type T2 struct {
    field2 string
    T1
}
type I interface {
    M()
}
func main() {
    var i I = &T2{"foo", T1{field1: "bar"}}
    i.M()
    fmt.Println(i.(*T2).field1) // barbar
}

在这个实例中,类型 *T2 实现了接口 I。被 *T1 实现的方法 M 作为了 T2 的一个内置的字段。在过去的文章里有更多关于字段和方法的详细介绍。

接口类型作为 map 中的 key 或者 value

map 是一个由 key-value 组成的数据结构。( 在 Go1.8 之前,map 底层是通过哈希表实现的 )

counters := make(map[string]int64)
counters["foo"] = 1
counters["bar"] += 2
fmt.Println(counters) // map[foo:1 bar:2]
delete(counters, "bar")
fmt.Println(counters) // map[foo:1]
fmt.Println(counters["bar"]) // 0
if _, ok := counters["bar"]; !ok {
    fmt.Println("'bar' not found")
}
counters["bar"] = 2
for key, value := range counters {
    fmt.Printf("%s: %v\n", key, value) // order is randomized!
}

接口类型的值可以作为 map 中的 key 或者 value 来使用:

type T1 struct {
    name string
}
func (t T1) M() {}
type T2 struct {
    name string
}
func (t T2) M() {}
type I interface {
    M()
}
func main() {
    m := make(map[I]int)
    var i1 I = T1{"foo"}
    var i2 I = T2{"bar"}
    m[i1] = 1
    m[i2] = 2
    fmt.Println(m) // map[{foo}:1 {bar}:2]
}

无所不在的接口

error

Go 中内置的 error 是一个接口类型:

type error interface {
    Error() string
}

任何类型实现了 Error 方法,此方法没有参数且返回一个 strin g 类型的值,那么这个类型就实现了 error 接口:

import "fmt"
type MyError struct {
    description string
}
func (err MyError) Error() string {
    return fmt.Sprintf("error: %s", err.description)
}
func f() error {
    return MyError{"foo"}
}
func main() {
    fmt.Printf("%v\n", f()) // error: foo
}

io.Writer

io.Writer 接口仅仅含有一个方法 —— Write:

Write(p []byte) (n int, err error)

如果有任何异常发生,返回的 error 就将不会是 nil。error 接口在前一节同样做了描述。Writer 接口在标准库中到处都有被用到,比如 MultiWriter、TeeReader、net/http,还有很多其他用到的地方。

点赞以帮助别人发现这篇文章。如果你想得到新文章的更新,请关注我。

资源

保留部分版权

Golang Programming Software Development Education Polymorphism

喜欢读吗?给 Michał Łowicki 一些掌声吧。

简单鼓励下还是大喝采,根据你对这篇文章的喜欢程度鼓掌吧。


via: https://medium.com/golangspec/interfaces-in-go-part-iii-61f5e7c52fb5

作者:Michał Łowicki  译者:xmge  校对:polaris1119

本文由 GCTT 原创编译,Go语言中文网 荣誉推出


有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1278 次点击  
加入收藏 微博
被以下专栏收入,发现更多相似内容
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传