Go学习笔记(13)Go接口

x33g5p2x  于2022-03-06 转载在 其他  
字(3.1k)|赞(0)|评价(0)|浏览(582)

Go的接口

Go的接口定义了一组方法(方法集),但不包含这些方法的具体实现。接口提供了一种方式来说明某类对象具有的行为,它的主要特点如下:

  • 接口实际上就是一组方法声明的集合,没有具体实现,没有字段属性
  • 某个类型只要实现了某个接口的所有方法,就实现了该接口,不需要显示声明实现了什么接口
  • 接口可以嵌入其它的接口

接口的定义和使用

定义接口的形式:

  1. type InterfaceName interface {
  2. Method1(param_list) return_type
  3. Method2(param_list) return_type
  4. }

下面是一个接口使用的实例,定义了一个Shaper的接口,它包含了Area()Perimeter两个方法;再定义了一个Square类型,它实现了上述具体两个方法,从而继承了接口。在主程序中,就可以创建一个Square类型的变量赋值给接口Shaper

  1. //定义接口
  2. type Shaper interface {
  3. Area() float64
  4. Perimeter() float64
  5. }
  6. //定义类型正方形
  7. type Square struct {
  8. side float64
  9. }
  10. //实现接口中的方法Area
  11. func (sq Square) Area() float64 {
  12. return sq.side * sq.side
  13. }
  14. //实现接口中的方法Perimeter
  15. func (sq Square) Perimeter() float64 {
  16. return sq.side * 4
  17. }
  18. func main() {
  19. var sq Shaper
  20. sq = Square{
  21. side: 2,
  22. }
  23. fmt.Println(sq.Area())
  24. }

要注意的是,当对象赋值给接口时(sq=Square{side:2,}),会对对象进行拷贝,再赋值给接口,接口中保存的是指向这个拷贝的地址指针。为了性能考虑,我们也可以将对象的指针赋值给接口,这样就不需要拷贝整个原始对象,只拷贝对象地址,地址还是指向原来的对象。上面的代码可以改成:

  1. //定义接口
  2. type Shaper interface {
  3. Area() float64
  4. Perimeter() float64
  5. }
  6. //定义类型正方形
  7. type Square struct {
  8. side float64
  9. }
  10. //实现接口中的方法Area
  11. func (sq *Square) Area() float64 {
  12. return sq.side * sq.side
  13. }
  14. //实现接口中的方法Perimeter
  15. func (sq *Square) Perimeter() float64 {
  16. return sq.side * 4
  17. }
  18. func main() {
  19. var sq Shaper
  20. sq = &Square{
  21. side: 2,
  22. }
  23. }

此外,还需要注意到几点:

  • 多个类型可以实现同一个接口
  • 实现某个接口的类型除了实现接口要求实现的方法外,还可以有其它的方法
  • 一个类型可以实现多个接口

在接口中内嵌接口

在Go接口的声明中同样可以嵌入一个接口,实现该外层接口的类型同样需要实现内嵌接口声明的方法

  1. type Shaper interface {
  2. Area() float64
  3. Perimeter() float64
  4. infoPrint //嵌入接口
  5. }
  6. type infoPrint interface {
  7. PrintInfo()
  8. }
  9. //计算面积
  10. func (sq Square) Area() float64 {
  11. return sq.side * sq.side
  12. }
  13. //计算周长
  14. func (sq Square) Perimeter() float64 {
  15. return sq.side * 4
  16. }
  17. //实现内嵌接口的方法
  18. func (sq Square) PrintInfo() {
  19. fmt.Println("我是一个正方形")
  20. }
  21. func main() {
  22. var sq Shaper
  23. sq = Square{
  24. side: 2,
  25. }
  26. }

接口转换

不同的接口之间可以进行转换,转换的原则是大接口转小接口,即将拥有内嵌子接口的接口转换为内嵌子接口

  1. type Shaper interface {
  2. Area() float64
  3. Perimeter() float64
  4. infoPrint //嵌入接口
  5. }
  6. type infoPrint interface {
  7. PrintInfo()
  8. }
  9. //省略方法实现
  10. func main(){
  11. var sq Shaper
  12. sq = Square{
  13. side: 2,
  14. }
  15. var ip infoPrint
  16. ip = infoPrint(sq) //这里将Shaper转换为inforPrint
  17. ip.PrintInfo()
  18. }

空接口

如果一个接口中不含任何的方法,那么该接口是个空接口,所有的类型都实现了空接口,它相当于所有类型的基类,类似于Java中的Object

  1. type Empty interface {}

可以给一个空接口的变量赋值任何类型

类型断言

一个接口类型的变量中可能包含着不同实际类型的值(实现接口的可以有不同的类型),当我们需要判断接口变量中的实际类型时,可以使用类型断言来检测
    假设i是一个接口变量,T是某个具体实现该接口的类型,那么可以使用下面的语句来检测i是否为类型T

  1. s, ok := i.(T)

当接口变量i的实际类型是T时,si转换到类型T的值,ok的值是true;当接口变量i的实际类型不是T时,s是类型T的零值,okfalse。下面是一段检测接口类型的实例:

  1. type Shaper interface {
  2. Area() float64
  3. Perimeter() float64
  4. }
  5. type Square struct {
  6. side float64
  7. }
  8. //计算面积
  9. func (sq Square) Area() float64 {
  10. return sq.side * sq.side
  11. }
  12. //计算周长
  13. func (sq Square) Perimeter() float64 {
  14. return sq.side * 4
  15. }
  16. func main(){
  17. sq := Square{
  18. side: 2,
  19. }
  20. isShape(sq)
  21. }
  22. //判断接口中的数据类型
  23. func isShape(s Shaper) {
  24. if sp, ok := s.(Square); ok {
  25. fmt.Println(sp, "is a shaper")
  26. } else {
  27. fmt.Println("none")
  28. }
  29. }

接口变量的类型也可以用一种type-switch的形式去检测:

  1. type Shaper interface {
  2. Area() float64
  3. Perimeter() float64
  4. }
  5. type Square struct {
  6. side float64
  7. }
  8. func (sq Square) Area() float64 {
  9. return sq.side * sq.side
  10. }
  11. func (sq Square) Perimeter() float64 {
  12. return sq.side * 4
  13. }
  14. func main(){
  15. sq := Square{
  16. side: 2,
  17. }
  18. whatType(sq)
  19. }
  20. func whatType(s interface{}) {
  21. switch v := s.(type) {
  22. case Square:
  23. fmt.Println(v, "is a square")
  24. case int:
  25. fmt.Println(v, "is a square")
  26. default:
  27. fmt.Println("none")
  28. }
  29. }

在函数whatType中,对接口变量的类型进行检测,函数传入的是一个空接口类型的参数,v = s.(type)得到变量s的具体类型,然后对case中列举的类型进行匹配,如果被检测的类型没有在case语句列举的类型中,就会执行default语句。但注意type-switch中不允许使用fallthrough

相关文章