[关闭]
@adamhand 2019-02-05T18:40:53.000000Z 字数 3440 阅读 778

golang--接口(2)


实现接口:指针接受者与值接受者

可以使用值接受者(Value Receiver)来实现接口,同样可以使用指针接受者(Pointer Receiver)来实现接口。

  1. package main
  2. import "fmt"
  3. type Describer interface {
  4. Describe()
  5. }
  6. type Person struct {
  7. name string
  8. age int
  9. }
  10. func (p Person) Describe() { // 使用值接受者实现
  11. fmt.Printf("%s is %d years old\n", p.name, p.age)
  12. }
  13. type Address struct {
  14. state string
  15. country string
  16. }
  17. func (a *Address) Describe() { // 使用指针接受者实现
  18. fmt.Printf("State %s Country %s", a.state, a.country)
  19. }
  20. func main() {
  21. var d1 Describer
  22. p1 := Person{"Sam", 25}
  23. d1 = p1
  24. d1.Describe()
  25. p2 := Person{"James", 32}
  26. d1 = &p2
  27. d1.Describe()
  28. var d2 Describer
  29. a := Address{"Washington", "USA"}
  30. /* 如果下面一行取消注释会导致编译错误:
  31. cannot use a (type Address) as type Describer
  32. in assignment: Address does not implement
  33. Describer (Describe method has pointer
  34. receiver)
  35. */
  36. //d2 = a
  37. d2 = &a // 这是合法的
  38. // 因为在第 22 行,Address 类型的指针实现了 Describer 接口
  39. d2.Describe()
  40. }

使用值接受者声明的方法,既可以用值来调用,也能用指针调用。不管是一个值,还是一个可以解引用的指针,调用这样的方法都是合法的。所以d1既可以使用值接收者调用p1的Describe()方法,也可以使用指针接受者调用p2的Describe()方法。

在上面程序里,如果去掉第 45 行的注释,我们会得到编译错误:main.go:42: cannot use a (type Address) as type Describer in assignment: Address does not implement Describer (Describe method has pointer receiver)。这是因为在第 22 行,我们使用 Address 类型的指针接受者实现了接口 Describer,而接下来我们试图用 a 来赋值 d2。然而 a 属于值类型,它并没有实现 Describer 接口。你应该会很惊讶,因为我们曾经学习过,使用指针接受者的方法,无论指针还是值都可以调用它。那么为什么第 45 行的代码就不管用呢?

其原因是:对于使用指针接受者的方法,用一个指针或者一个可取得地址的值来调用都是合法的。但接口中存储的具体值(Concrete Value)并不能取到地址,因此在第 45 行,对于编译器无法自动获取 a 的地址,于是程序报错。

程序正常运行的结果如下:

  1. Sam is 25 years old
  2. James is 32 years old
  3. State Washington Country USA

实现多个接口

类型可以实现多个接口。

  1. package main
  2. import "fmt"
  3. type SalaryCalculator interface {
  4. displaySalary()
  5. }
  6. type LeaveCalculator interface {
  7. calculatorLeavesLeft() int
  8. }
  9. type Employee struct {
  10. firstName string
  11. lastName string
  12. basicPay int
  13. pf int
  14. totalLeaves int
  15. leavesToken int
  16. }
  17. func (e Employee)displaySalary() {
  18. fmt.Printf("%s %s has salary $%d\n", e.firstName, e.lastName, (e.pf + e.basicPay))
  19. }
  20. func (e Employee)calculatorLeavesLeft() int {
  21. return e.totalLeaves - e.leavesToken
  22. }
  23. func main() {
  24. e := Employee{
  25. firstName:"bob",
  26. lastName:"steven",
  27. basicPay:3000,
  28. pf:2000,
  29. totalLeaves:10,
  30. leavesToken:3,
  31. }
  32. var s SalaryCalculator = e
  33. s.displaySalary()
  34. var l LeaveCalculator = e
  35. fmt.Printf("\nleaves left = %d", l.calculatorLeavesLeft())
  36. }

该程序会输出:

  1. bob steven has salary $5000
  2. leaves left = 7

接口的嵌套

尽管 Go 语言没有提供继承机制,但可以通过嵌套其他的接口,创建一个新接口。

  1. package main
  2. import "fmt"
  3. type SalaryCalculator interface {
  4. displySalary()
  5. }
  6. type LeavesCalculator interface {
  7. calculatorLeavesLeft() int
  8. }
  9. type EmployeeOprations interface {
  10. SalaryCalculator
  11. LeavesCalculator
  12. }
  13. type Employee struct {
  14. firstName string
  15. lastName string
  16. basicPay int
  17. pf int
  18. totalLeaves int
  19. leavesToken int
  20. }
  21. func (e Employee)displySalary() {
  22. fmt.Printf("%s %s has %d salary\n", e.firstName, e.lastName, (e.pf + e.basicPay))
  23. }
  24. func (e Employee)calculatorLeavesLeft() int {
  25. return e.totalLeaves - e.leavesToken
  26. }
  27. func main() {
  28. e := Employee{
  29. firstName:"bob",
  30. lastName:"steven",
  31. basicPay:4000,
  32. pf:1000,
  33. totalLeaves:10,
  34. leavesToken:5,
  35. }
  36. var empOp EmployeeOprations = e
  37. empOp.displySalary()
  38. fmt.Printf("\nleaves left = %d", empOp.calculatorLeavesLeft())
  39. }

在 46 行,empOp 的类型是 EmployeeOperations,e 的类型是 Employee,我们把 empOp 赋值为 e。接下来的两行,empOp 调用了 DisplaySalary() 和 CalculateLeavesLeft() 方法。

该程序输出:

  1. bob steven has 5000 salary
  2. leaves left = 5

接口的零值

接口的零值是 nil。对于值为 nil 的接口,其底层值(Underlying Value)和具体类型(Concrete Type)都为 nil。

  1. package main
  2. import "fmt"
  3. type Describer interface {
  4. describe()
  5. }
  6. func main() {
  7. var d1 Describer
  8. if d1 == nil{
  9. fmt.Printf("d1 is nil and has type %T value %v", d1, d1)
  10. }
  11. }

改程序输出:

  1. d1 is nil and has type <nil> value <nil>

对于值为 nil 的接口,由于没有底层值和具体类型,当我们试图调用它的方法时,程序会产生 panic 异常。

  1. package main
  2. type Describer interface {
  3. Describe()
  4. }
  5. func main() {
  6. var d1 Describer
  7. d1.Describe()
  8. }

在上述程序中,d1 等于 nil,程序产生运行时错误 panic: panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0xffffffff addr=0x0 pc=0xc8527]

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注