在Go语言中,接口类型是由一组方法定义的集合。
一个类型是否实现了一个接口,就看这个类型是否实现了接口中定义的所有方法。在go语言中,无需特别的指明。

定义一个接口

  1. type Abser interface {
  2. Abs() float64
  3. }

定义一个结构体

  1. type Vertex struct {
  2. X, Y float64
  3. }

定义两个方法,一个是结构体指针,一个是结构体

  1. func (v *Vertex) Abs() float64 {
  2. return v.X * v.X + v.Y * v.Y
  3. }
  4. func (v Vertex) Scale() float64 {
  5. return v.X + v.Y
  6. }

声明一个接口变量

  1. var a Abser

结构体实例化

  1. f := Vertex(3, 4)

指针也是Vertex结构体的指针,所以可以用f来实例化。

  1. a = &f

分别看一下a和f都能实现什么方法。

  1. fmt.Println(f.Abs())
  2. fmt.Println(f.Scale())
  3. fmt.Println(a.Abs())
  4. fmt.Println(a.Scale())

仔细测试,你会发现 fmt.Println(a.Scale()) 是会报错的“a.Scale undefined (type Abser has no field or method Scale)”。是的,a 没有 Scale() 这个方法。

为什么呢?因为 func (v Vertex) Scale() float64 里的是 Vertex 而不是 Vertex 。
如果你加上
符号,那么,a.Scale() 就可以实现了。

完整例子

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. type Abser interface {
  6. Abs() float64
  7. }
  8. type Vertex struct {
  9. X, Y float64
  10. }
  11. func (v *Vertex) Abs() float64 {
  12. return v.X * v.X + v.Y * v.Y
  13. }
  14. func (v Vertex) Scale() float64 {
  15. return v.X + v.Y
  16. }
  17. func main() {
  18. var a Abser
  19. f := Vertex{3, 4}
  20. a = &f
  21. fmt.Println(f.Abs())
  22. fmt.Println(f.Scale())
  23. fmt.Println(a.Abs())
  24. //fmt.Println(a.Scale())
  25. }

运行结果

  1. 25
  2. 7
  3. 25

结合上边的例子,我们可以发现,类型通过实现方法来实现接口,却不必要显示的声明。所有没有关键字implements。这是隐式接口。

隐式接口解耦了实现接口的包和定义接口的包,实现包和定义包”互不依赖”。

Stringers一个普遍存在的接口,在fmt中定义。

  1. type Stringer interface{
  2. String() string
  3. }

我们给它在包内依附一个结构体,定义一个String()方法

  1. type Cofox struct {
  2. name string
  3. }
  4. func (c *Cofox) String() string {
  5. return "Joel " + c.name
  6. }

为了区别原始的值,我们在 Strings() 内的返回值前加了一个字符串 Joel ,以作区别。

完整代码

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. type Stringer interface {
  6. String() string
  7. }
  8. type Cofox struct {
  9. name string
  10. }
  11. func (c *Cofox) String() string {
  12. return "Joel " + c.name
  13. }
  14. func main() {
  15. var S Stringer
  16. c := Cofox{"Smith"}
  17. S = &c
  18. fmt.Println(S.String())
  19. fmt.Println(c.String())
  20. fmt.Println(c.name)
  21. }

运行结果如下

  1. Joel Smith
  2. Joel Smith
  3. Smith

接口和结构体都可以使用 String() 函数方法。

你可是试着把 String() 方法里的返回值写成

  1. return fmt.Sprintf("full name is Joel %v", c.name)

运行自己看看结果有无不同。
再写一个例子,这次结构体多加一个字段,看看如何应用。

  1. package main
  2. import "fmt"
  3. type Person struct {
  4. Name string
  5. Age int
  6. }
  7. func (p Person) String() string {
  8. return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
  9. }
  10. func main() {
  11. a := Person{"Joel", 31}
  12. z := Person{"Smith", 45}
  13. fmt.Println(a, z)
  14. }

运行结果

  1. Joel (31 years) Smith (45 years)

Go 接口 - 图1