Go语言中的接口和现实生活中的USB插槽很像, 它定义某种标准, 但不关心具体实现。

Go的接口是一种数据类型,它只包括没有实现的方法名,Go 语言中的接口是隐式实现的,也就是说,如果一个类型实现了一个接口定义的所有方法,那么它就自动地实现了该接口。因此,我们可以通过将接口作为参数来实现对不同类型的调用,从而实现多态

定义接口格式

type 接口名称 interface{
  函数声明
}

实例

// 1.定义一个接口
type usber interface {
 start()
 stop()
}

//定义结构体Computer
type Computer struct {
 name string
 model string
}
// 2.实现接口中的所有方法
func (cm Computer)start() {
 fmt.Println("启动电脑")
}
func (cm Computer)stop() {
 fmt.Println("关闭电脑")
}

//定义结构体Phone
type Phone struct {
 name string
 model string
}
// 2.实现接口中的所有方法
func (p Phone)start()  {
 fmt.Println("启动手机")
}
func (p Phone)stop()  {
 fmt.Println("关闭手机")
}

// 3.使用接口定义的方法
func working(u usber)  {
 u.start()
 u.stop()
}

//主函数
func main() {
 cm := Computer{"戴尔", "F1234"}
 working(cm) // 启动电脑 关闭电脑

 p := Phone{"华为", "M10"}
 working(p)  // 启动手机 关闭手机
}

观察以上代码,我们发现对接口的调用是通过一个working函数实现的。也就是说我们我们并不是通过结构体来实现的而是通过接口本身来实现的。

可以这样理解:

我们定义了1个接口A,里面有个空方法F。然后我们用结构体B绑定方法的方式重写实现了这个空方法F,也就间接的实现了这个接口。一般我们调用的是时候,会直接用原始的接口A的实例去调用这个方法F,而不是这个结构体B的实例去调用F。

接下来我们对比一下结构体实现和接口实现的异同

结构体实现

// 1.定义一个接口
type usber interface {
	start()
	stop()
}
type Computer struct {
	name  string
	model string
}

// 2.实现接口中的所有方法
func (cm Computer) start() {
	fmt.Println("启动电脑")
}
func (cm Computer) stop() {
	fmt.Println("关闭电脑")
}

//3.结构体实现
func main() {
	cm := Computer{"戴尔", "F1234"}
	cm.start() // 启动电脑
	cm.stop() //关闭电脑
}

 接口实现

// 1.定义一个接口
type usber interface {
	start()
	stop()
}
type Computer struct {
	name  string
	model string
}

// 2.实现接口中的所有方法
func (cm Computer) start() {
	fmt.Println("启动电脑")
}
func (cm Computer) stop() {
	fmt.Println("关闭电脑")
}

// 3.接口实现
func main() {
	var a usber
	a = Computer{"Dell", "F1001"}
	a.start()
	a.stop()
}

 实际上他们得出结果是一致的都是

启动电脑
关闭电脑

这时候细心的盆友可能发现了,第一种实现的情况下usber接口根本没有被使用,他依然能够得同样的结果,那接口存在的意义是什么呢???

这里笔者基于自己目前的理解是,主要是为了实现类似于其他语言多态的效果,并且提供了一种非侵入式的规范(隐式实现),我目前的理解不一定完全正确,笔者目前也在寻找更合理的解释,欢迎大家在评论区讨论。

这里是一点参考资料

但是结论是毋庸置疑的,那就是我们需要通过接口类型来实现接口中的方法,而不是通过结构体。


接口需要注意的点

type usber interface {
   func start(){ // 错误
    fmt.Println("启动")
   }
   func stop()  { // 错误
    fmt.Println("停止")
   }
  }
  type usber interface {
   name string // 错误
   start()
   stop()
  }
  // 1.定义一个接口
  type usber interface {
   start()
   stop()
  }
  type Computer struct {
   name string
   model string
  }
  // 2.实现接口中的所有方法
  func (cm Computer)start() {
   fmt.Println("启动电脑")
  }
  func (cm Computer)stop() {
   fmt.Println("关闭电脑")
  }
  // 2.只实现了接口中部分方法
  type Phone struct {
   name string
   model string
  }
  func (p Phone)start()  {
   fmt.Println("启动手机")
  }
  func main() {
   // 1.定义一个usber接口类型变量
   var i usber
   // 2.用usber接口类型变量接收Computer类型结构体
   i = Computer{"戴尔", "F1234"} // 实现了所有方法, 不会报错
   // 3.用usber接口类型变量接收Phone类型结构体
   i = Phone{"华为", "M10"} // 只实现了部分方法, 会报错
 
  }
  type studier interface {
   read()
  }
  type Person struct {
   name string
   age int
  }
  func (p Person)read()  {
   fmt.Println(p.name, "正在学习")
  }
  func main() {
   // 1.定义一个接口类型变量
   var s studier
   // 2.用接口类型变量接收实现了接口的结构体
   s = Person{"weber", 23}
   s.name = "zzz" // 报错, 由于s是接口类型, 所以不能访问属性

  }
type studier interface {
    read()
}
type Person struct {
    name string
    age int
}
func (p Person)read()  {
    fmt.Println(p.name, "正在学习")
}
func main() {
    var s studier
    s = Person{"weber", 23}
    s.name = "zzz" // 报错, 由于s是接口类型, 所以不能访问属性
    // 2.定义一个结构体类型变量
    //var p Person
    // 不能用强制类型转换方式将接口类型转换为原始类型
    //p = Person(s) // 报错

    // 2.利用ok-idiom模式将接口类型还原为原始类型
    // s.(Person)这种格式我们称之为: 类型断言
    if p, ok := s.(Person); ok {
     p.name = "zzz"
     fmt.Println(p)
    }

    // 2.通过 type switch将接口类型还原为原始类型
   // 注意: type switch不支持fallthrought
    switch p := s.(type) {
     case Person:
      p.name = "zzz"
      fmt.Println(p) // {zzz 23}
     default:
      fmt.Println("不是Person类型")
    }
}