Go 语言快速入门指南:接口(二)

95 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第 11 天,点击查看活动详情

7. 接口类型

接口类型具体描述了一系列方法的集合,一个实现了这些方法的具体类型是这个接口类型的实例

io.Writer类型

io.Writer类型是用得最广泛的接口之一,因为它提供了所有类型的写入bytes的抽象,包括文件类型,内存缓冲区,网络链接,HTTP客户端,压缩工具,哈希等等。io包中定义了很多其它有用的接口类型。Reader可以代表任意可以读取bytes的类型,Closer可以是任意可以关闭的值,例如一个文件或是网络链接。

package io
type Reader interface {
    Read(p []byte) (n int, err error)
}
type Closer interface {
    Close() error
}

type ReadWriter interface {
    Reader
    Writer
}
type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

flag 类型

需要实现 flag.Value接口的类型

package flag

// Value is the interface to the value stored in a flag.
type Value interface {
    String() string
    Set(string) error
}

8. 接口值

概念上讲一个接口的值,接口值,由两个部分组成,一个具体的类型和那个类型的值。它们被称为接口的动态类型和动态值。

定义了变量w,类型和值都是 nil

var w io.Writer

w.Write([]byte("hello")) // panic: nil pointer dereference

将一个os.File类型的值赋给变量 w ,这个赋值过程调用了一个具体类型到接口类型的隐式转换,这和显式的使用io.Writer(os.Stdout)是等价的。这个接口值的动态类型被设为os.File指针的类型描述符,它的动态值持有os.Stdout的拷贝

w = os.Stdout

调用一个包含*os.File类型指针的接口值的Write方法,使得(*os.File).Write方法被调用。这个调用输出“hello”
w.Write([]byte("hello")) // "hello"

效果和下面这个直接调用一样:
os.Stdout.Write([]byte("hello")) // "hello"

给接口值赋了一个bytes.Buffer类型的值,现在动态类型是bytes.Buffer并且动态值是一个指向新分配的缓冲区的指针

w = new(bytes.Buffer)

Write方法的调用也使用了和之前一样的机制:
w.Write([]byte("hello")) // writes "hello" to the bytes.Buffers

这次类型描述符是*bytes.Buffer,所以调用了(*bytes.Buffer).Write方法,并且接收者是该缓冲区的地址。这个调用把字符串“hello”添加到缓冲区中。

nil赋给了接口值,这个重置将它所有的部分都设为nil值,把变量w恢复到和它之前定义时相同的状态

nil赋给了接口值

9. 接口嵌套

接口与接口间可以通过嵌套创造出新的接口。

例如:飞鱼,既可以飞,又可以游泳。我们创建一个飞Fly接口,创建一个游泳接口Swim,飞鱼接口有这两个接口组成。

飞 Flyer 接口

type Flyer interface {
    fly()
}

创建 Swimmer 接口

type Swimmer interface {
    swim()
}

组合一个接口 FlyFish

type FlyFish interface {
    Flyer
    Swimmer
}

创建一个结构体 Fish

type Fish struct {}

实现这个组合接口

func (fish Fish) fly() {
    fmt.Println("fly...")
}

func (fish Fish) swim() {
    fmt.Println("swim...")
}

嵌套得到的接口的使用与普通接口一样。

例子代码:

func main() {
    var ff FlyFish
    ff = Fish{}
    ff.fly()
    ff.swim()
}

运行结果

fly...
swim...

10. 空接口

10.1 定义

空接口是指没有定义任何方法的接口。因此任何类型都实现了空接口。

空接口类型的变量可以存储任意类型的变量。

func main() {
    // 定义一个空接口x
    var x interface{}
    s := "pprof.cn"
    x = s
    fmt.Printf("type:%T value:%v\n", x, x)
    i := 100
    x = i
    fmt.Printf("type:%T value:%v\n", x, x)
    b := true
    x = b
    fmt.Printf("type:%T value:%v\n", x, x)
}

10.2 应用

空接口作为函数的形参

使用空接口实现可以接收任意类型的函数参数。

// 空接口作为函数参数
func show(a interface{}) {
    fmt.Printf("type:%T value:%v\n", a, a)
}

空接口作为 map 的值

var studentInfo = make(map[string]interface{})
studentInfo["name"] = "程序员小乔"
studentInfo["age"] = 18
studentInfo["married"] = false

11. 类型断言

具体类型的类型断言从它的操作对象中获得具体的值。如果检查失败,接下来这个操作会抛出 panic

var w io.Writer
w = os.Stdout
f := w.(*os.File)     
c := w.(*bytes.Buffer)

对一个接口类型的类型断言改变了类型的表述方式,改变了可以获取的方法集合(通常更大),但是它保留了接口值内部的动态类型和值的部分。

var w io.Writer
w = os.Stdout
rw := w.(io.ReadWriter) // success
w = new(ByteCounter)
rw = w.(io.ReadWriter) // panic

下一篇我们继续探索 Go 语言更多知识。敬请期待!

12. 知识星球

星球地址:t.zsxq.com/03MJM7YfI

13. 关注公众号「程序员小乔」