接口是一种重要的类型,他是一组肯定的方法集合。html
一个接口变量能够存储任何实现了接口方法的具体值。
一个重要的例子就是io.Reader和io.Writergolang
type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte)(n int, err error) }
若是一个方法实现了io.Reader或者io.Writer里的方法,那么它便实现了io.Reader 或 io.Writer 。 这就less
意味着一个io.Reader 变量能够持有任何一个实现了Read方法的类型的值 [1]ide
例 1:
var r io.Reader
r = os.Stdin
os.Stdin 在 os 包中定义:函数
var ( Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin") Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout") )
NewFile 返回的是一个 *File 结构体,在 os/types.go, File 结构体代码,里面是一个匿名的file结构体ui
// File represents an open file descriptor.
type File struct { *file // os specific
}
file也是一个结构体,os/file_plan9.gospa
type file struct { fd int name string dirinfo *dirInfo // nil unless directory being read
}
这个 File 结构体实现了 Read(p []byte) (n int, err error) 方法, 而这个恰好是接口 io.Reader 里的方法
因此os.Stdin 能够赋值给 io.Readercode
例 2:
var r io.Reader
r = bufio.NewReader(r)htm
在包 bufio.goblog
// NewReader returns a new Reader whose buffer has the default size.
func NewReader(rd io.Reader) *Reader { return NewReaderSize(rd, defaultBufSize) }
func NewReaderSize(rd io.Reader, size int) *Reader { // Is it already a Reader?
b, ok := rd.(*Reader) if ok && len(b.buf) >= size { return b } if size < minReadBufferSize { size = minReadBufferSize } r := new(Reader) r.reset(make([]byte, size), rd) return r }
2)上面[1]中下划线 接口变量能够持有任何一个实现了Read方法的类型的值,那么接口变量又是什么呢?
变量里是具体值和这个值类型
3)一个类型实现了 interface 中的全部方法,咱们就说该类型实现了该 interface,
因此全部的类型都实现了 empty interface,由于任何类型至少有0个方法。
go中没有显示的关键字来实现interface,只须要实现interface包含的方法便可
interface类型定义
type interfaceNamer interface { Method1(param_list) return_type Method2(param_list) return_type ... }
按照约定,只包含一个方法的)接口的名字由方法名加 [e]r 后缀组成,例如 Printer、Reader、Writer、Logger、Converter 等等。还有一些不经常使用的方式(当后缀 er 不合适时),好比 Recoverable,此时接口名以 able 结尾,或者以 I 开头(像 .NET 或 Java 中那样)
接口能够屏蔽内部细节,和具体的实现方法。只关注我须要作什么,而不关注怎么作
示例:
package main import ( "fmt" ) type Ager interface { Get() int Set(int) } type Student struct { Age int } func (s Student) Get() int { return s.Age } func (s *Student) Set(age int) { s.Age = age } func funone(ager Ager) { ager.Set(10) fmt.Println(ager.Get()) } func main() { //=== 第一部分 stu 能够做为一个变量传递给函数,由于它做为一个struct实现了接口里面的方法
stu := Student{} funone(&stu) //=== 第二部分 i 做为一个接口变量,它存储了实现了这个接口方法的类型的值
var i Ager i = &stu fmt.Println(i.Get()) }
在上面程序中,定义来一个名为Ager的 interface 接口,里面定义了2个方法Get() , Set(), 定义了一个 student 的结构体,而且结构体实现了 interface 里面的2个方法Get,Set,咱们就说结构体实现了 这个接口
定义了一个 funcone 的函数,里面参数ager是一个interface类型(interface类型做为函数参数),那么实现了这个接口的任何变量均可以做为参数传递给 funcone 了, (在 main 函数中 第一部分注释)
interface变量存储了实现者变量的值 (main函数中的 第二部分注释),因此它又能够调用结构体的方法
一个类型实现了接口里的全部方法,那么这个类型就实现了这个接口
一个接口能够包含一个或者多个其余接口
至关于直接将这些内嵌接口的方法加入到了外层接口中,至关于组合了方法
仍是拿最典型的 Reader 和 Writer 接口
type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) } type ReadWriter interface { Reader Writer }
若是一个类型既实现了 Reader 接口,也实现了Writer接口,那么它就自动实现了 ReadWriter 接口
接口和结构体的定义很类似,也能够完成嵌入接口的功能,嵌入的匿名的接口,能够自动的具有被嵌入的接口的方法
interface{} 空interface,它能存储任意类型的值, 它至关于 c 系的 void * 类型
定义一个空接口
var i interface{} s := "hello" num : 10 i = s i = num
空接口在打印包中的应用,它又是一个可变长参数 fmt/print.go
func Println(a ...interface{}) (n int, err error) { return Fprintln(os.Stdout, a...) }
既然接口类型可以实现多种类型的值,那么咱们应该有一种方式来检测这种动态类型的值,即运行时变量中存储的值的实际类型。 在执行过程当中动态类型可能会有所不一样,可是它老是能够分配给接口变量自己的类型。
那就是 类型断言
1):type-ok 形式
1.1:第一种
直接判断是不是某类型的变量 value,ok = element.(T), 这里value是变量的值,ok是bool类型, element必须是一个 interface 变量, T是断言的类型
1.2 第二种 简单形式:
value := element.(T)
示例:
package main import ( "fmt"
"math" ) type Square struct { side float32 } type Circle struct { radius float32 } type Shaper interface { Area() float32 } func main() { var area Shaper sq1 := new(Square) sq1.side = 5 area = sq1 //is Square the type of area
if t, ok := area.(*Square); ok { fmt.Printf("The type of area is: %T \n", t) } if u, ok := area.(*Circle); ok { fmt.Printf("The type of area is: %T \n", u) } else { fmt.Println("area does not cotain a variable of type Cicle") } //输出结果 // go run assert1.go // The type of area is: *main.Square // area does not cotain a variable of type Cicle
} func (sq *Square) Area() float32 { return sq.side * sq.side } func (ci *Circle) Area() float32 { return ci.radius * ci.radius * math.Pi }
2):switch 形式
上面也能够用 switch的形式
switch t := area.(type) { case *Square: fmt.Printf("Type Square %T with value %v\n", t, t) case *Circle: fmt.Printf("Type Circle %T with value %v\n", t, t) case nil: fmt.Printf("nil value: nothing to check?\n") default: fmt.Printf("Unexpected type %T\n", t) }
参考:
1. http://wiki.jikexueyuan.com/project/the-way-to-go/11.4.html
2. https://research.swtch.com/interfaces
3. https://blog.golang.org/laws-of-reflection