over-golang/01-基础语法/12-接口类型-1-接口的使用.md
2021-07-02 18:11:59 +08:00

4.8 KiB
Raw Permalink Blame History

一 接口 interface

接口interface是调用方和实现方均需要遵守的一种约束约束开发者按照统一的方法命名、参数类型、数量来处理具体业务。实际上接口就是一组没有实现的方法声明到某个自定义类型要使用该方法时根据具体情况把这些方法实现出来。接口语法

type 接口类型名 interface {
	方法名1(参数列表) 返回值列表
	方法名2(参数列表) 返回值列表
	...
}

示例:

package main

import "fmt"

// 运输方式
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)
}

// 只要实现了 Transporter接口的类型都可以作为参数
func trans(t Transporter) {
	t.BicycleTran()
}

注意:

  • Go语言的接口在命名时一般会在单词后面添加er如写操作的接口叫做Writer
  • 当方法名首字母大写,且实现的接口首字母也是大写,则该方法可以被接口所在包之外的代码访问
  • 方法与接口中的方法签名一致(方法名、参数列表、返回列表都必须一致)
  • 参数列表和返回值列表中的变量名可以被忽略type writer interfae{ Write([]byte) error}
  • 接口中所有的方法都必须被实现
  • 如果编译时发现实现接口的方法签名不一致,则会报错:does not implement

二 Go接口的特点

在上述示例中Go无须像Java那样显式声明实现了哪个接口即为非侵入式接口编写者无需知道接口被哪些类型实现接口实现者只需要知道实现的是什么样子的接口但无需指明实现了哪个接口。编译器知道最终编译时使用哪个类型实现哪个接口或者接口应该由谁来实现。

类型和接口之间有一对多和多对一的关系,即:

  • 一个类型可以实现多个接口,接口间是彼此独立的,互相不知道对方的实现
  • 多个类型也可以实现相同的接口。
type Service interface {
	Start()
	Log(string)
}

// 日志器
type Logger struct {
}
//日志输出方法
func (g *Logger) Log(s string){
	fmt.Println("日志:", s)
}

// 游戏服务
type GameService struct {
	Logger
}
// 实现游戏服务的Start方法
func (g *GameService) Start() {
	fmt.Println("游戏服务启动")
}

func main() {
	s := new(GameService)
	s.Start()
	s.Log("hello")
}

在上述案例中,即使没有接口也能运行,但是当存在接口时,会隐式实现接口,让接口给类提供约束。

使用接口调用了结构体中的方法,也可以理解为实现了面向对象中的多态。

三 接口嵌套

Go中不仅结构体之间可以嵌套接口之间也可以嵌套。接口与接口嵌套形成了新的接口只要接口的所有方法被实现则这个接口中所有嵌套接口的方法均可以被调用。

// 定义一个 写 接口
type Writer interface {
	Write(p []byte) (n int, e error)
}

// 定义一个 读 接口
type Reader interface {
	Read() error
}

// 定义一个 嵌套接口
type IO interface {
	Writer
	Closer
}

四 空接口

4.1 空接口定义

空接口是接口的特殊形式,没有任何方法,因此任何具体的类型都可以认为实现了空接口。

	var any interface{}

	any = 1
	fmt.Println(any)

	any = "hello"
	fmt.Println(any)

空接口作为函数参数:

func Test(i interface{}) {
	fmt.Printf("%T\n", i)
}

func main() {
	Test(3)			// int
	Test("hello")	// sting
}

利用空接口,可以实现任意类型的存储:

	m := make(map[string]interface{})
	m["name"] = "李四"
	m["age"] = 30	

4.2 从空接口获取值

保存到空接口的值,如果直接取出指定类型的值时,会发生编译错误:

	var a int = 1
	var i interface{} = a
	var b int = i				//这里编译报错类型不一致可以这样做b := i

4.3 空接口值比较

类型不同的空接口比较:

	var a interface{} = 100
	var b interface{} = "hi"

	fmt.Println(a == b)			//false

不能比较空接口中的动态值:

	var c interface{} = []int{10}
	var d interface{} = []int{20}
	fmt.Println(c == d)					//运行报错

空接口的类型和可比较性:

类型 说明
map 不可比较,会发生宕机错误
切片 不可比较,会发生宕机错误
通道 可比较必须由同一个make生成即同一个通道才是true
数组 可比较,编译期即可知道是否一致
结构体 可比较,可诸葛比较结构体的值
函数 可比较