基本介绍

如果一个类型实现了某个接口,所有使用这个接口的地方,都可以支持这种类型的值。
interface 类型可以定义一组方法,并且 interface 不能包含任何变量。
接口是用来定义行为的类型。这些被定义的行为不由接口直接实现,而是通过方法由用户定义的类型实现。
(行为就是方法,由用户定义的类型实现)
好处: 多态高内聚、低耦合
高内聚:可以把代码全部写到函数里面去。
低耦合: 各司其职,各自负责自己的,(处理自己的错误,不要抛给别人)。

语法

  1. type 接口名 interface {
  2. // 不能在这里定义变量
  3. method1(参数列表) (返回值列表)
  4. method2(参数列表) (返回值列表)
  5. }
  6. // 接口定义的行为由用户定义的类型实现方法(实现所有的行为)
  7. func (t 自定义类型) method1(参数列表) (返回值列表) {
  8. // 方法时实现
  9. }
  10. func (t 自定义类型) method2(参数列表) (返回值列表) {
  11. // 方法时实现
  12. }
  13. // method1 method2 是签名,必须跟接口里定义的行为保持一致

USB接口例子

  1. package main
  2. import "fmt"
  3. // 定义 Usb 接口, 有两个方法(行为)
  4. type Usb interface {
  5. start()
  6. stop()
  7. }
  8. // 定义 Phone 结构体, 并实现 start, stop 方法
  9. type Phone struct {
  10. Name string
  11. }
  12. // 值接收者
  13. // Phone 类型实现了 Usb 接口 (就是实现了 Usb 接口的所有方法)
  14. func (phone Phone) start() {
  15. fmt.Printf("%v开始--- \n", phone.Name)
  16. }
  17. func (phone Phone) stop() {
  18. fmt.Printf("%v停止--- \n", phone.Name)
  19. }
  20. // 定义 Camera 结构体, 并实现 start, stop 方法
  21. type Camera struct {
  22. Name string
  23. }
  24. // 指针接收者
  25. // Camera 指针类型实现了 Usb 接口 (就是实现了 Usb 接口的所有方法)
  26. func (camera *Camera) start() {
  27. fmt.Printf("%v开始--- \n", camera.Name)
  28. }
  29. func (camera *Camera) stop() {
  30. fmt.Printf("%v停止--- \n", camera.Name)
  31. }
  32. // 定义 Computer 结构体, 并实现 Working 方法
  33. type Computer struct {
  34. }
  35. // 编写 Working 方法,接收一个 Usb 接口类型的变量,
  36. // 只要一个类型实现了某个接口,那么所有使用这个接口的地方,都支持这种类型的值
  37. // (比如: 实参 phone, camera, 都是 Usb 类型来接收, 这就是多态)
  38. func (computer *Computer) Working(usb Usb) {
  39. usb.start()
  40. usb.stop()
  41. }
  42. func main() {
  43. computer := Computer{}
  44. phone := Phone{"手机"}
  45. camera := Camera{"相机"}
  46. computer.Working(phone) // 值接收者
  47. computer.Working(&camera) // 指针接收者(因为是 *Camera 类型实现的 Usb 接口)
  48. /*
  49. 手机开始---
  50. 手机停止---
  51. 相机开始---
  52. 相机停止---
  53. */
  54. }

接口实现

接口是用来定义行为的类型。这些被定义的行为不由接口直接实现,而是通过方法由用户定义的类型实现。如果用户定义的类型实现了某个接口类型声明的一组方法,那么这个用户定义的类型的值就可以赋给这个接口类型的值。这个赋值会把用户定义的类型的值存入接口类型的值。
对接口值方法的调用会执行接口里存储的用户定义的类型的值对应的方法。因为任何用户定义的类型都可以实现任何接口,所以对接口值方法的调用自然就是一种多态,在这个关系里,用户定义的类型通常叫作实体类型,原因是如果离开内部存储的用户定义的类型的值的实现,接口值并没有具体的行为。

Go官方例子

  1. package main
  2. import "fmt"
  3. // notifier 是一个定义了通知类行为的接口
  4. type notifier interface {
  5. notify()
  6. }
  7. // user 在程序里定义一个用户类型
  8. type user struct {
  9. name string
  10. email string
  11. }
  12. // notify 是使用指针接收者实现的 notifier 接口
  13. func (u *user) notify() {
  14. fmt.Printf("Sending user email to %s<%s> \n",
  15. u.name,
  16. u.email)
  17. }
  18. func main() {
  19. // 创建一个 user 类型的值,并发送通知
  20. u := user{"Bill", "bill@email.com"}
  21. // sendNotification(u) // 传入值, 值并没有实现 notifier 接口
  22. /*
  23. 报错:
  24. cannot use u (type user) as type notifier in argument to sendNotification:
  25. user does not implement notifier (notify method has pointer receiver)
  26. */
  27. sendNotification(&u) // 传入地址
  28. // Sending user email to Bill<bill@email.com>
  29. }
  30. // sendNotification 接收一个实现了 notifier 接口的值,并发送通知
  31. func sendNotification(n notifier) {
  32. n.notify()
  33. }

方法集

用户定义的类型的值或者指针要满足接口的实现,需要遵守一些规则。
规范里描述的方法集:

  1. Values Methods Receivers
  2. ---------------------------------------------------------
  3. T (t T)
  4. *T (t T) and (t *T)

从接收者类型角度看方法集:

  1. Methods Receivers Values
  2. ---------------------------------------------------------
  3. (t T) T and *T
  4. (t *T) *T

多态

多态是指代码可以根据类型的具体实现采取不同行为的能力

Go方法例子

  1. // 多态例子
  2. package main
  3. import "fmt"
  4. // notifier 是一个定义了通知类行为的接口
  5. type notifier interface {
  6. notify()
  7. }
  8. // user 在程序里定义一个用户类型
  9. type user struct {
  10. name string
  11. email string
  12. }
  13. // notify 是使用指针接收者实现的notifier接口
  14. func (u *user) notify() {
  15. fmt.Printf("Sending user email to %s<%s> \n",
  16. u.name,
  17. u.email)
  18. }
  19. // -------------以下代码是相对于接口demo新增的,展示多态--------------
  20. // admin 在程序里定义一个用户类型
  21. type admin struct {
  22. name string
  23. email string
  24. }
  25. // notify 是使用指针接收者实现的notifier接口
  26. func (u *admin) notify() {
  27. fmt.Printf("Sending admin email to %s<%s> \n",
  28. u.name,
  29. u.email)
  30. }
  31. func main() {
  32. // 创建一个 user 类型的值,并传递给 sendNotification
  33. bill := user{"Bill", "bill@email.com"}
  34. sendNotification(&bill)
  35. // 创建一个 admin 类型的值,并传递给 sendNotification
  36. lisa := admin{"Lisa", "lisa@email.com"}
  37. sendNotification(&lisa)
  38. }
  39. // sendNotification 接收一个实现了 notifier 接口的值,并发送通知
  40. // 多态函数
  41. func sendNotification(n notifier) {
  42. n.notify()
  43. }

代码解读

声明了 user 和 admin 两个结构,都实现了 notifier 接口(两个实体类型实现了 notifier 接口)
声明了多态函数 sendNotification, 这个函数接收一个实现了 notifier 接口的值作为参数。既然任意一个实体类型都能实现该接口,那么这个函数可以针对任意实体类型的值来执行 notifier 方法,因此这个函数就能提供多态的行为。可以同时执行user和admin实现的行为。

  1. func main() {
  2. // 创建一个 user 类型的值,并传递给 sendNotification
  3. bill := user{"Bill", "bill@email.com"}
  4. sendNotification(&bill)
  5. // 创建一个 admin 类型的值,并传递给 sendNotification
  6. lisa := admin{"Lisa", "lisa@email.com"}
  7. sendNotification(&lisa)
  8. }

接口使用细节

  1. 接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)
  2. 接口中的所有方法都没有方法体(即没有直接实现方法)
  3. 在Golang中,一个自定义类型需要将某个接口的所有方法都实现,我们称这个自定义类型实现了该接口
  4. 只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型。
  5. 一个自定义类型可以实现多个接口
  6. 一个接口(比如A接口)可以继承多个别的接口(比如B、C接口),这时如果要实现A接口,也必须将B、C接口的方法全部实现。
  7. interface类型默认是一个指针(引用类型),如果没有初始化interface,则会输出 nil
  8. 空接口 interface{} 没有任何方法(行为),所以,所有类型都实现了空接口
    1. var blank02 interface{} //

image.png

代码案例一

  1. package main
  2. import "fmt"
  3. type AInterface interface {
  4. Say()
  5. }
  6. type Student struct {
  7. Name string
  8. }
  9. // 如果 Student 没有实现 Say() 方法,则调用时报错
  10. // Student does not implement AInterface (missing Say method)
  11. func (stu Student) Say() {
  12. fmt.Printf("name= %v \n", stu.Name)
  13. }
  14. type cusInt int
  15. // 数值类型实现接口
  16. func (i cusInt) Say() {
  17. fmt.Println("我是数值类型")
  18. }
  19. func main() {
  20. var a AInterface
  21. fmt.Printf("a类型: %T, 值: %v \n", a, a)
  22. // 直接调用接口的方法 报错
  23. // 需要通过自定义类型实现方法
  24. // a.Say() // panic: runtime error: invalid memory address or nil pointer dereference
  25. // Student 类型实现了 AInterface 接口定义的所有方法(即实现了接口)
  26. // 所以, Student 的实例 stu 可以赋值给 AInterface 类型
  27. var stu Student = Student{"tom"}
  28. var a1 AInterface = stu
  29. a1.Say() // name= tom
  30. // 数值类型调用接口
  31. var i cusInt
  32. callSay(i)
  33. }
  34. func callSay(a AInterface) {
  35. a.Say()
  36. }

代码案例二

  1. // 接口继承,以及实现,空接口
  2. package main
  3. import "fmt"
  4. type BInterface interface {
  5. test01()
  6. }
  7. type CInterface interface {
  8. test02()
  9. }
  10. // 要实现 AInterface 接口,必要同时实现 BInterface、CInterface接口
  11. type AInterface interface {
  12. BInterface
  13. CInterface
  14. test03()
  15. }
  16. type Student struct {
  17. }
  18. func (stu Student) test01() {}
  19. func (stu Student) test02() {}
  20. func (stu Student) test03() {}
  21. type blankInterface interface{}
  22. func main() {
  23. var stu Student
  24. var a AInterface = stu
  25. a.test01()
  26. // 空接口
  27. var b blankInterface
  28. var blank blankInterface = 10
  29. fmt.Printf("b类型: %T, 值: %v \n", b, b) // b类型: <nil>, 值: <nil>
  30. fmt.Printf("blank类型: %T, 值: %v \n", blank, blank) // blank类型: int, 值: 10
  31. // 直接定义空接口
  32. var blank02 interface{}
  33. fmt.Printf("blank02类型: %T, 值: %v \n", blank02, blank02) // blank02类型: <nil>, 值: <nil>
  34. }

嵌入类型(继承与接口)

type embedding

  1. 通过嵌入类型,与内部类型相关的标识符会提升到外部类型上。(类似于JavaScript中的原型链)
  2. 这个外部类型就组合了内部类型包含的所有属性,并且可以添加新的字段和方。
  3. 外部类型也可以通过声明于内部类型标识符同名的标识符来覆盖内部标识符的字段或者方法,这就是扩展或者修改已有类型的方法 ```go // 这个示例程序展示如何将一个类型嵌入另一个类型 // 内部类型和外部类型之间的关系 package main

import “fmt”

// user 在程序里定义一个用户类型 type user struct { name string email string }

// notify 实现了一个可以通过user类型值得指针调用得方法 func (u *user) notify() { fmt.Printf(“Sending user email to %s<%s> \n”, u.name, u.email) }

// admin 代表一个拥有权限得管理员用户 // 要嵌入一个类型,只需要声明这个类型的名字就可以了 // user 是外部类型 admin 的内部类型 type admin struct { user // 嵌入类型 level string }

func main() { // 创建一个 admin 用户 // 内部类型的初始化采用结构字面量的方式完成 ad := admin{ user: user{ name: “join”, email: “join@yahoo.com”, }, level: “super”, } // 直接访问内部类型的方法 ad.user.notify() // 内部类型的方法也被提升到外部类型 ad.notify()

  1. /*
  2. 输出
  3. Sending user email to join<join@yahoo.com>
  4. Sending user email to join<join@yahoo.com>
  5. */

}

  1. <a name="OHonH"></a>
  2. ## 嵌入类型接口实现
  3. ```go
  4. // 演示如何将嵌入类型应用于接口
  5. package main
  6. import "fmt"
  7. // notifier 是一个定义了通知类行为的接口
  8. type notifier interface {
  9. notify()
  10. }
  11. // user 在程序里定义一个用户类型
  12. type user struct {
  13. name string
  14. email string
  15. }
  16. // notify 是使用指针接收者实现的notifier接口
  17. func (u *user) notify() {
  18. fmt.Printf("Sending user email to %s<%s> \n",
  19. u.name,
  20. u.email)
  21. }
  22. // admin 代表一个拥有权限得管理员用户
  23. // 要嵌入一个类型,只需要声明这个类型的名字就可以了
  24. // user 是外部类型 admin 的内部类型
  25. type admin struct {
  26. user // 嵌入类型
  27. level string
  28. }
  29. func main() {
  30. // 创建一个 admin 用户
  31. // 内部类型的初始化采用结构字面量的方式完成
  32. ad := admin{
  33. user: user{
  34. name: "join",
  35. email: "join@yahoo.com",
  36. },
  37. level: "super",
  38. }
  39. // 给 admin 用户发送一个通知
  40. // 用于实现接口的内部类型的方法,被提升到外部类型
  41. sendNotification(&ad)
  42. }
  43. // sendNotification 接收一个实现了 notifier 接口的值,并发送通知
  44. func sendNotification(n notifier) {
  45. n.notify()
  46. }
  47. // Sending user email to join<join@yahoo.com>

外部类型与内部类型实现同一个接口

此时,实现接口的内部类型的方法,不会被提升到外部类型。

  1. // 这个示例程序展示当内部类型和外部类型要实现同一个接口的做法
  2. package main
  3. import "fmt"
  4. // notifier 是一个定义了通知类行为的接口
  5. type notifier interface {
  6. notify()
  7. }
  8. // user 在程序里定义一个用户类型
  9. type user struct {
  10. name string
  11. email string
  12. }
  13. // 通过 user 类型的指针调用的方法
  14. func (u *user) notify() {
  15. fmt.Printf("Sending user email to %s<%s> \n",
  16. u.name,
  17. u.email)
  18. }
  19. // admin 代表一个拥有权限得管理员用户
  20. type admin struct {
  21. user // 嵌入类型
  22. level string
  23. }
  24. // 通过 admin 类型的指针调用的方法
  25. func (u *admin) notify() {
  26. fmt.Printf("Sending admin email to %s<%s> \n",
  27. u.name,
  28. u.email)
  29. }
  30. func main() {
  31. // 创建一个 admin 用户
  32. ad := admin{
  33. user: user{
  34. name: "john",
  35. email: "john@yahoo.com",
  36. },
  37. level: "super",
  38. }
  39. // 给 admin 用户发送通知
  40. // 接口嵌入的内部类型实现并没有提升到外部类型
  41. sendNotification(&ad) // Sending admin email to john<john@yahoo.com>
  42. // 我们可以直接访问内部类型的方法
  43. ad.user.notify() // Sending user email to john<john@yahoo.com>
  44. // 内部类型的方法没有被提升
  45. ad.notify() // Sending admin email to john<john@yahoo.com>
  46. }
  47. // sendNotification 接收一个实现了 notifier 接口的值,并发送通知
  48. func sendNotification(n notifier) {
  49. n.notify()
  50. }