1、接口 interface
接口是调用方和实现方均需要遵守的一种约束,约束开发者按照统一的方法命名、参数类型、数量来处理具体业务。实际上,接口就是一组没有实现的方法声明,到某个自定义类型要使用该方法时,根据具体情况把这些方法实现出来。接口语法:  
1 2 3 4 5
   | type 接口类型名 interface { 	方法名1(参数列表) 返回值列表 	方法名2(参数列表) 返回值列表 	... }
 
  | 
 
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
   |  type Transporter interface { 	BicycleTran() 	CarTran() }
 
  type Driver struct { 	Name string 	Age  int }
 
  func (d *Driver) BicycleTran() { 	fmt.Println("使用自行车运输") } func (d *Driver) CarTran() { 	fmt.Println("使用小汽车运输") }
  func main() { 	d := &Driver{ 		"张三", 		27, 	} 	trans(d) }
 
  func trans(t Transporter) { 	t.BicycleTran() }
 
  | 
 
注意:
- Go 语言的接口在命名时,一般会在单词后面添加 er,如写操作的接口叫做 Writer
 
- 当方法名首字母大写,且实现的接口首字母也是大写,则该方法可以被接口所在包之外的代码访问
 
- 方法与接口中的方法签名一致(方法名、参数列表、返回列表都必须一致)
 
- 参数列表和返回值列表中的变量名可以被忽略,如:type writer interfae{ Write([]byte) error}
 
- 接口中所有的方法都必须被实现
 
- 如果编译时发现实现接口的方法签名不一致,则会报错:
does not implement。 
2、Go接口的特点
在上述示例中,Go无须像Java那样显式声明实现了哪个接口,即为非侵入式,接口编写者无需知道接口被哪些类型实现,接口实现者只需要知道实现的是什么样子的接口,但无需指明实现了哪个接口。编译器知道最终编译时使用哪个类型实现哪个接口,或者接口应该由谁来实现。  
类型和接口之间有一对多和多对一的关系,即:
- 一个类型可以实现多个接口,接口间是彼此独立的,互相不知道对方的实现
 
- 多个类型也可以实现相同的接口。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   | type Service interface { 	Start() 	Log(string) }
 
  type Logger struct { }
  func (g *Logger) Log(s string){ 	fmt.Println("日志:", s) }
 
  type GameService struct { 	Logger }
  func (g *GameService) Start() { 	fmt.Println("游戏服务启动") }
  func main() { 	s := new(GameService) 	s.Start() 	s.Log("hello") }
 
  | 
 
在上述案例中,即使没有接口也能运行,但是当存在接口时,会隐式实现接口,让接口给类提供约束。
使用接口调用了结构体中的方法,也可以理解为实现了面向对象中的多态。  
3、接口嵌套
Go 中不仅结构体之间可以嵌套,接口之间也可以嵌套。接口与接口嵌套形成了新的接口,只要接口的所有方法被实现,则这个接口中所有嵌套接口的方法均可以被调用。  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   |  type Writer interface { 	Write(p []byte) (n int, e error) }
 
  type Reader interface { 	Read() error }
 
  type IO interface { 	Writer 	Closer }
 
  | 
 
4、空接口
4.1、空接口定义
空接口是接口的特殊形式,没有任何方法,因此任何具体的类型都可以认为实现了空接口。
1 2 3 4 5 6 7
   | var any interface{}
  any = 1 fmt.Println(any)
  any = "hello" fmt.Println(any)
 
  | 
 
空接口作为函数参数:
1 2 3 4 5 6 7 8
   | func Test(i interface{}) { 	fmt.Printf("%T\n", i) }
  func main() { 	Test(3)			 	Test("hello")	 }
 
  | 
 
利用空接口,可以实现任意类型的存储:
1 2 3
   | m := make(map[string]interface{}) m["name"] = "李四" m["age"] = 30
 
  | 
 
4.2、从空接口获取值
保存到空接口的值,如果直接取出指定类型的值时,会发生编译错误:
1 2 3
   | var a int = 1 var i interface{} = a var b int = i			
 
  | 
 
4.3、空接口值比较
类型不同的空接口比较:
1 2 3 4
   | var a interface{} = 100 var b interface{} = "hi"
  fmt.Println(a == b)			
 
  | 
 
不能比较空接口中的动态值:
1 2 3
   | var c interface{} = []int{10} var d interface{} = []int{20} fmt.Println(c == d)					
 
  | 
 
空接口的类型和可比较性:
| 类型 | 
说明 | 
| map | 
不可比较,会发生宕机错误 | 
| 切片 | 
不可比较,会发生宕机错误 | 
| 通道 | 
可比较,必须由同一个make生成,即同一个通道才是true | 
| 数组 | 
可比较,编译期即可知道是否一致 | 
| 结构体 | 
可比较,可逐个比较结构体的值 | 
| 函数 | 
可比较 |