接口| 青训营;

138 阅读3分钟

目前为止,我们看到的类型都是具体的类型。一个具体的类型可以准确的描述它所代表的值 并且展示出对类型本身的一些操作方式就像数字类型的算术操作,切片类型的索引、附加和 取范围操作。具体的类型还可以通过它的方法提供额外的行为操作。总的来说,当你拿到一 个具体的类型时你就知道它的本身是什么和你可以用它来做什么。 在Go语言中还存在着另外一种类型:接口类型。接口类型是一种抽象的类型。它不会暴露出 它所代表的对象的内部值的结构和这个对象支持的基础操作的集合;它们只会展示出它们自 己的方法。也就是说当你有看到一个接口类型的值时,你不知道它是什么,唯一知道的就是 可以通过它的方法来做什么。

接口类型具体描述了一系列方法的集合,一个实现了这些方法的具体类型是这个接口类型的 实例。 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 
}

上面用到的语法和结构内嵌相似,我们可以用这种方式以一个简写命名另一个接口,而不用 声明它所有的方法。这种方式本称为接口内嵌。尽管略失简洁,我们可以像下面这样,不使 用内嵌来声明io.Writer接口。

type ReadWriter interface {
Read(p []byte) (n int, err error)
Write(p []byte) (n int, err error)
}

上面3种定义方式都是一样的效果。方法的顺序变化也没有影响,唯一重要的就是这个集合里 面的方法。

我们会学到另一个标准的接口类型flag.Value是怎么帮助命令行标记定义新的符号 的。

var period = flag.Duration("period", 1*time.Second, "sleep period")
func main() {
flag.Parse()
fmt.Printf("Sleeping for %v...", *period)
time.Sleep(*period)
fmt.Println() 
}

在它休眠前它会打印出休眠的时间周期。fmt包调用time.Duration的String方法打印这个时间 周期是以用户友好的注解方式,而不是一个纳秒数字:

$ go build gopl.io/ch7/sleep
$ ./sleep
Sleeping for 1s...

默认情况下,休眠周期是一秒,但是可以通过 -period 这个命令行标记来控制。flag.Duration 函数创建一个time.Duration类型的标记变量并且允许用户通过多种用户友好的方式来设置这 个变量的大小,这种方式还包括和String方法相同的符号排版形式。这种对称设计使得用户交 互良好。

$ ./sleep -period 50ms
Sleeping for 50ms...
$ ./sleep -period 2m30s
Sleeping for 2m30s... 
$ ./sleep -period 1.5h
Sleeping for 1h30m0s... 
$ ./sleep -period "1 day" invalid value "1 day" for flag -period: time: invalid duration 1 day