接口约定
接口也是一种类型和其他类型不同接口是一种抽象的类型他不会暴露出所代表对象的内部结构,只展示他的方法,即你只知道它用来做什么不知道是为什么。
接口类型
接口类型具体描述了一系列方法的集合,一个实现了这些方法的具体类型是这个接口类型的实例。方法名字的集合类似于结构体。也可以嵌套
格式:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
type Test interface{
Read(p []byte) (n int, err error)
Close() error
}
实现接口的条件
接口指定的规则非常简单:表达一个类型属于某个接口只要这个类型实现这个接口,一个类型如果拥有一个接口需要的所有方法,那么这个类型就实现了这个接口。
一个有更多方法的接口,与一个更少方法的接口相比,更多方法的接口类型会告诉我们更多关于它的值持有的信息,并且对实现它的类型要求更加严格。
有一种特殊的接口interface{}没有任何方法,因为空接口类型对实现它的类型没有要求,所以我们可以将任意一个值赋给空接口类型。对于创建的一个interface{}值持有一个boolean,float,string,map,pointer,或者任意其它的类型,因为interface{}中没有任何方法我们也可以理解为所有类型都实现了他,我们当然不能直接对它持有的值做操作,因为interface{}没有任何方法。interface{}可以做参数,可以在slice和map中使用。
接口值
概念上讲一个接口的值分为两部分:一部分是类型,一部分是类型对应的值,他们分别叫:动态类型和动态值。Go语言中,变量总是被一个定义明确的值初始化,即使接口类型也不例外。对于一个接口的零值就是它的类型和值的部分都是nil。一个接口为空是他的动态类型和动态值一样都为空时接口值才为空。通常在编译期,我们不知道接口值的动态类型是什么,所以一个接口上的调用必须使用动态分配。因为不是直接进行调用。
类型断言
法上它看起来像x.(T)被称为断言类型,这里x表示一个接口的类型和T表示一个类型。一个类型断言检查它操作对象的动态类型是否和断言的类型匹配,就是获得接口的方法。断言有两种情况:
一种是具体类型的类型断言从它的操作对象中获得具体的值。如果检查失败,接下来这个操作会抛出panic。
另一种是,如果断言的类型T是一个接口类型,然后类型断言检查是否x的动态类型满足T。如果这个检查成功了,动态值没有获取到;这个结果仍然是一个有相同类型和值部分的接口值,但是结果有类型T。换句话说,对一个接口类型的类型断言改变了类型的表述方式,改变了可以获取的方法集合(通常更大),但是它保护了接口值内部的动态类型和值的部分。如果断言操作的对象是一个nil接口值,那么不论被断言的类型是什么这个类型断言都会失败。
经常地我们对一个接口值的动态类型是不确定的,并且我们更愿意去检验它是否是一些特定的类型。如果类型断言出现在一个预期有两个结果的赋值操作中,例如如下的定义,这个操作不会在失败的时候发生panic但是代替地返回一个额外的第二个结果,这个结果是一个标识成功的布尔值。
var w io.Writer = os.Stdout
f, ok := w.(*os.File) // success: ok, f == os.Stdout
b, ok := w.(*bytes.Buffer) // failure: !ok, b == nil