经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
Go语言的接口详解
来源:jb51  时间:2021/10/11 9:22:29  对本文有异议

接口就是一系列方法的集合(规范行为)

在面向对象的领域里,接口一般这样定义:接口定义一个对象的行为,规范子类对象的行为。

在 Go 语言中的接口是非侵入式接口(接口没了,不影响代码),侵入式接口(接口没了,子类报错)

Go 也是鸭子类型,比如我现在有个鸭子类,内有 speak 方法和 run 方法,子类只要实现了 speak 和 run,我就认为子类是鸭子,我只要子类中有这两个方法你就是鸭子,有这两个方法你就是鸭子,他是从下往上推导只要有你这里面的东西,那就是算是继承了你这个接口

1、接口的用途

接口是一个类型

  1. // Duck 定义一个鸭子接口
  2. type Duck interface {
  3. speak()
  4. run()
  5. }
  6.  
  7. // WhiteDuck 定义一个白鸭子结构体
  8. type WhiteDuck struct {
  9. name string
  10. age int
  11. sex string
  12. }
  13.  
  14. // BlackDuck 定义一个黑鸭子结构体
  15. type BlackDuck struct {
  16. name string
  17. age int
  18. sex string
  19. }
  20.  
  21. // 让白鸭子和黑鸭子绑定接口中的所有方法,就叫实现该接口
  22. // 让白鸭子实现 Duck 接口
  23. func (w WhiteDuck) speak() {
  24. fmt.Println("白鸭子嘎嘎叫,它的名字叫", w.name)
  25. }
  26.  
  27. func (w WhiteDuck) run() {
  28. fmt.Println("白鸭子慢悠悠的走,它的名字叫", w.name)
  29. }
  30.  
  31. // 让黑鸭子实现 Duck 接口
  32. func (b BlackDuck) speak() {
  33. fmt.Println("黑鸭子呱呱叫,它的名字叫", b.name)
  34. }
  35.  
  36. func (b BlackDuck) run() {
  37. fmt.Println("黑鸭子歪歪扭扭的走,它的名字叫", b.name)
  38. }
  39.  
  40.  
  41. func main() {
  42. var duck Duck
  43. duck = WhiteDuck{"小白", 15, "男"} // 把我的对象赋值给一个接口类型,就可以实现多态的效果
  44. fmt.Println(duck)
  45. // duck 现在他是一个接口,它只能取方法,不能取出属性了。
  46. duck.speak()
  47. duck.run()
  48. }
  49.  
  50.  

// 输出:
{小白 15 男}
白鸭子嘎嘎叫,它的名字叫 小白
白鸭子慢悠悠的走,它的名字叫 小白

2、类型断言

用于提取接口的底层值,就是把接口类型转成 struct ,属性,自有方法也有了。

  1. func main() {
  2. var duck Duck = WhiteDuck{"小白", 15, "男"}
  3. // 断言是 WhiteDuck 类型
  4. value, ok := duck.(WhiteDuck)
  5. // 断言成功,ok=true,value就是WhiteDuck结构体对象
  6. fmt.Println(value) // 输出:{小白 15 男}
  7. fmt.Println(value.name) // 输出:小白
  8. fmt.Println(ok) // 输出:true
  9.  
  10. // 断言失败,ok1=false,value1是BlackDuck类型的空值,因为没有赋值
  11. value1, ok1 := duck.(BlackDuck)
  12. fmt.Println(value1) // 输出:{ 0 }
  13. fmt.Println(ok1) // 输出:false
  14. }

3、类型选择

(通过 Type Switch )

用于将接口的具体类型与很多 case 语句所指定的类型进行比较。

  1. func main() {
  2. var duck Duck = WhiteDuck{"小白", 15, "男"}
  3. test(duck)
  4. }
  5.  
  6. func test(duck Duck) {
  7. switch value := duck.(type) {
  8. case WhiteDuck:
  9. fmt.Println(value.name)
  10. fmt.Println("我是白鸭子")
  11. case BlackDuck:
  12. fmt.Println(value.name)
  13. fmt.Println("我是黑鸭子")
  14. default:
  15. fmt.Println(value)
  16. fmt.Println("我是鸭子这个类")
  17. }
  18. }

4、空接口

没有任何方法,所有数据类型都实现了空接口

  1. type Empty interface {} // 空接口
  2.  
  3. func main() {
  4. var a int = 10
  5. var b string = "XiaoYang"
  6. var c [3]int
  7. var e Empty // e是空接口类型,可以接受任意的数据类型
  8. e = a
  9. e = b
  10. e = c
  11. // 这样的话需要把它类型选择回来
  12. // 正常情况下我只能接收 Empty 类型的,但是 a b c 都不是 Empty 类型的
  13. test(a) // 输出:我是int 10
  14. test(b) // 输出:我是字符串 XiaoYang
  15. test(c) // 输出:我是数组 [0 0 0]
  16. }
  17.  
  18. // 如果这不是一个空接口,比如是 Duck 那么只要实现了 Duck 接口的所有数据类型都可以传
  19. func test(b Empty) {
  20. switch v:=b.(type) {
  21. case string:
  22. fmt.Println("我是字符串", v)
  23. case int:
  24. fmt.Println("我是int", v)
  25. case [3]int:
  26. fmt.Println("我是数组", v)
  27. }
  28. }

5、匿名空接口

没有名字的空接口,一般用在形参上

  1. func main() {
  2. var duck Duck = WhiteDuck{"小白", 15, "男"}
  3. test(10)
  4. test("XiaoYang")
  5. test(duck)
  6. }
  7.  
  8. // 这叫匿名空接口,所有数据类型都可以往里面传,如果想用原来的结构体还需要类型选择回来才能用
  9. func test(b interface{}) {
  10. fmt.Println(b)
  11. }

6、实现多个接口

  1. // Duck 定义一个鸭子接口
  2. type Duck interface {
  3. speak()
  4. run()
  5. }
  6.  
  7. type Animal interface {
  8. eat()
  9. sleep()
  10. }
  11.  
  12. // WhiteDuck 定义一个白鸭子结构体
  13. type WhiteDuck struct {
  14. name string
  15. age int
  16. sex string
  17. }
  18.  
  19.  
  20. // 让白鸭子即实现 Duck 接口也实现了 Animal 接口
  21. func (w WhiteDuck) speak() {
  22. fmt.Println("白鸭子嘎嘎叫,它的名字叫", w.name)
  23. }
  24.  
  25. func (w WhiteDuck) run() {
  26. fmt.Println("白鸭子慢悠悠的走,它的名字叫", w.name)
  27. }
  28.  
  29. func (w WhiteDuck) eat() {
  30. fmt.Println("白鸭子吃饭,它的名字叫", w.name)
  31. }
  32.  
  33. func (w WhiteDuck) sleep() {
  34. fmt.Println("白鸭子睡觉,它的名字叫", w.name)
  35. }
  36.  
  37.  
  38. func main() {
  39. var w WhiteDuck = WhiteDuck{}
  40. var a Animal
  41. var d Duck
  42.  
  43. // 这样的话我的 w 即可以给 a ,也可以给 d
  44. // 但是一旦转到某个接口上,只能使用该接口的方法,自身属性和自身方法需要类型断言后才能使用
  45. a = w // w 给了 a ,那么 a 就只能调用 Animal 接口的方法
  46. a.sleep()
  47. a.eat()
  48. d = w // w 给了 d ,那么 a 就只能调用 Duck 接口的方法
  49. d.run()
  50. d.speak()
  51. }

7、接口嵌套

  1. type Duck interface {
  2. Animal // Duck 嵌套 Animal 接口
  3. speak()
  4. run()
  5. }
  6.  
  7. type Animal interface {
  8. eat()
  9. sleep()
  10. }
  11.  
  12. type WhiteDuck struct {
  13. name string
  14. age int
  15. sex string
  16. }
  17.  
  18.  
  19. // 这样白鸭子即实现 Duck 接口也实现了 Animal 接口
  20. func (w WhiteDuck) speak() {
  21. fmt.Println("白鸭子嘎嘎叫,它的名字叫", w.name)
  22. }
  23.  
  24. func (w WhiteDuck) run() {
  25. fmt.Println("白鸭子慢悠悠的走,它的名字叫", w.name)
  26. }
  27. func (w WhiteDuck) eat() {
  28. fmt.Println("白鸭子嘎嘎叫,它的名字叫", w.name)
  29. }
  30.  
  31. func (w WhiteDuck) sleep() {
  32. fmt.Println("白鸭子慢悠悠的走,它的名字叫", w.name)
  33. }
  34.  
  35.  
  36.  
  37. func main() {
  38. var a Animal
  39. var d Duck
  40. var w WhiteDuck = WhiteDuck{}
  41.  
  42. // w 即可以给 a,也可以给 d
  43. a = w // 但是 a 只能调用 Animal 中的两个方法
  44. a.sleep()
  45. a.eat()
  46.  
  47. d = w // d 却能调用 Duck 和 Animal 中的四个方法
  48. d.sleep()
  49. d.eat()
  50. d.speak()
  51. d.run()
  52. }

8、接口零值

  1. func main() {
  2.  
  3. var a Animal // nil 就是说明它是一个引用类型
  4. // 其内部表示就已经告诉了我们,它里面就存两个值,一个是它的类型,一个是指向具体值的指针
  5.  
  6. fmt.Println(a) // 输出:<nil>
  7. }

9、make和new的区别

  1. type WhiteDuck struct {
  2. name string
  3. sex string
  4. age int
  5. }
  6.  
  7. func main() {
  8. var per1 *WhiteDuck = new(WhiteDuck) // new 是返回指向这个类型的指针
  9. // 或者是我取 WhiteDuck 的地址,赋值给 per2
  10. var per2 = &WhiteDuck{}
  11.  
  12. fmt.Println(per1) // 输出:&{ 0}
  13. fmt.Println(per2) // 输出:&{ 0}
  14.  
  15. var per3 = make([]int, 3, 4) // make 是具体的创建引用类型
  16. // new 是创建指向这个类型的指针
  17. var per4 = new([]int) // 是一个指向切片类型的指针
  18.  
  19. fmt.Println(per3) // 输出:[0 0 0]
  20. fmt.Println(per4) // 输出:&[]
  21. }

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注w3xue的更多内容!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号