Go的interface接口

Go的interface接口

接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节

interface是一组method的集合,是duck-type programming的一种体现

只要一台机器有洗衣服和甩干的功能,我就称它为洗衣机。不关心属性(数据),只关心行为(方法)

接口(interface)是一种类型

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

type writer interface{
    Write([]byte) error
}

一个对象只要全部实现了接口中的方法,那么就实现了这个接口

接口类型变量能够存储所有实现了该接口的实例

// Say 接口
type Sayer interface {
  say
}
type dog struct {}
type cat struct {}
// dog实现了Sayer接口
func (d dog) say() {
  fmt.Println("汪汪汪")
}
// cat实现了Sayer接口
func (c cat) say() {
  fmt.Println("喵喵喵")
}
func main() {
  var x Sayer      // 声明一个Sayer类型的变量x
  a := cat{}       // 实例化一个cat
  b := dog{}       // 实例化一个dog
  x = a            // 直接把cat实例赋值给x
  x.say()          // 喵喵喵
  x = b            // 可以把dog实例直接赋值给x
  x.say()          // 汪汪汪
}

值接收者和指针接收者实现接口的区别

值接收者实现接口

type Mover interface {
  move()
}
type dog struct {}

// 值接收者实现接口
func (d dog) move() {
  fmt.Println("dog dog dog")
}
func main() {
  var x Mover
  var dahuang = dog{}     // 大黄是dog类型
  x = dahuang             // x可以接收dog类型
  var xiaohuang = &dog{}  // 小黄是*dog类型
  x = xiaohuang           // x可以接收*dog类型
  x.move()
}

使用值接收者实现接口之后,不管是dog结构体还是结构体指针*dog类型的变量都可以赋值给该接口变量

Go语言中有对指针类型变量求值的语法糖,dog指针xiaohuang内部会自动求值*xiaohuang

指针接收者实现接口

type Mover interface {
  move()
}
type dog struct {}

// 值接收者实现接口
func (d *dog) move() {
  fmt.Println("dog dog dog")
}
func main() {
  var x Mover
  var dahuang = dog{}     // 大黄是dog类型
  x = dahuang             // x不可以接收dog类型
  var xiaohuang = &dog{}  // 小黄是*dog类型
  x = xiaohuang           // x可以接收*dog类型
  x.move()
}

面试题: 下面的能通过编译吗?

type People interface {
  Speak(string) string
}
type Student struct{}
func (s *stu) Speak(think string) (talk string) {
  if think == "1" {
    talk = "3"
  } else {
    talk = "4"
  }
  return 
}
func main() {
  var peo People = &Student{}
  think := "5"
  fmt.Println(peo.Speak(think))
}

正确修改:

type People interface {
	Speak(string) string
}
type Student struct{}

func (s *Student) Speak(think string) (talk string) {
	if think == "1" {
		talk = "3"
	} else {
		talk = "4"
	}
	return
}
func main() {
	var peo People
	peo = &Student{}
	think := "5"
	fmt.Println(peo.Speak(think))
}

类型和接口的关系

  1. 一个类型可以实现多个接口
  2. 多个类型可以可以实现同一个接口

接口嵌套

// Sayer 接口
type Sayer interface {
  say()
}
// Mover 接口
type Mover interface {
  move()
}
// 接口嵌套
type Animal interface {
  Sayer
  Mover
}
type cat struct {
  name string
}
func (c cat) say() {
  fmt.Println("猫说:Hello World!")
}
func (c cat) move() {
  fmt.Println("猫动:~~~")
}
func main() {
  var x Animal
  x = cat{name: "小黑"}
  x.move()
  x.say()
}
// 猫动:~~~
// 猫说:Hello World!

空接口的应用

  1. 空接口作为函数的参数, 使用空接口实现可以接收任意类型的函数参数
func show(a interface{}) {
  fmt.Printf("type: %T, value: %v\n", a, a)
}
  1. 空接口作为map的值,使用空接口实现可以保存任意值的字典
func main() {
  var studentInfo = make(map[string]interface{})
  studentInfo["name"] = "james"
  studentInfo["age"] = 27
  studentInfo["married"] = false
  fmt.Println(studentInfo)
	fmt.Printf("%T\n", studentInfo["age"])
}
// map[name:james age:27 married:false]
// int

类型断言

空接口可以存储任意类型的值,那我们如何获取其存储的具体数据呢?

想要判断空接口中的值这个时候就可以使用类型断言,其语法格式

x.(T)
  • x:表示类型为interface{}的变量
  • T:表示断言x可能是的类型

该语法返回两个参数,第一个参数是x转化为T类型后的变量

第二个值是一个布尔值,若为true则表示断言成功,为false则表示断言失败

func main() {
  var x interface{}
  x = "Hello World!"
  v, ok := x.(string)
  if ok {
    fmt.Println(v)
  } else {
    fmt.Println("断言失败")
  }
}
// Hello World!

当需要断言多次时

func main() {
	var x interface{}
	x = "Hello World!"
	switch v := x.(type) {
	case string:
		fmt.Printf("x is a string, %v\n", v)
	  case int:
		fmt.Printf("x is a int, %v\n", v)
	  case bool:
		fmt.Printf("x is a bool, %v\n", v)
	  default:
		fmt.Println("unknow")
	}
}
// x is a string, Hello World!

因为空接口可以存储任意类型值的特点,所以空接口在Go语言中的使用十分广泛

只有当有两个或两个以上的具体类型必须以相同的方式进行处理时才需要定义接口

不要为了接口而写接口,那样只会增加不必要的抽象

posted @ 2020-01-09 17:07  _慕  阅读(116)  评论(0编辑  收藏  举报
Title
返回顶部