切片 里面包含interface_Golang - interface 注释笔记

切片 里面包含interface_Golang - interface 注释笔记_第1张图片
Interface —— defines the behavior of an object
The Interface only specifies what should do. It's a collection of Method Signature(方法签名)。
As for how to implement the behavior, the object itself determines; When a type defines all the methods in the interface, we call it implement the interface.

创建并实现接口

package main

import "fmt"

//interface definition, 创建了一个名为 VowelsFinder 的接口,该接口有一个 FindVowels() []rune 的方法。
type VowelsFinder interface {
      
  FindVowels() []rune
}

//MyString implements VowelsFinder
type MyString string

// 方法:在func 和 名儿中间加了一个receiver type,比如这个 MyString 在方法内部可以用ms指代MyString使用。
// 元素类型为rune的切片: []rune
// 给Receiver Type MyString 添加了方法 FindVowels() []rune,现在,我们称 MyString 实现了 VowelsFinder 接口
func (ms MyString) FindVowels() []rune {
      
  var vowels []rune
  for _, rune :=range ms {
      
    if rune == 'a' || rune == 'e' || rune == 'i' || rune == 'o' || rune == 'u' {
      
	   vowels = append(vowels, rune)
	}
  }
  return vowels
}

func main () {
      
  name := MyString("Sam Anderson") // name 的类型为 MyString
  var v VowelsFinder // v 的类型为 VowelsFinder
  v = name  // 我们把 name 赋值给了 v。由于 MyString 实现了 VowelFinder,因此这是合法的
  fmt.Print("Vowels are %c", v.FindVowels())
}

接口的实际用途

package main

import "fmt"

// 声明了一个 SalaryCalculator 接口类型,它只有一个方法 CalculateSalary() int。
type SalaryCalculator interface {
      
  CalculateSalary() int
}

type Permanent struct {
      
  empId     int
  basicpay  int
  pf        int
}

type Contract struct {
      
  empId int
  basicpay int
}

//salary of permanent employee is sum of basic pay and pf
func (p Permanent) CalculateSalary() int {
      
  return p.basicpay + p.pf
}

//salary of contract employee is the basic pay alone
func (c Contract) CalculateSalary() int {
      
  return c.basicpay
}

/*
total expense is calculated by iterating though the SalaryCalculator slice and summing  
the salaries of the individual employees  
*/
func totalExpense(s []SalaryCalculator) {
      
  expense :=0
  for _, v := range s {
      
    expense = expense + v.CalculateSalary()
  }
  fmt.Printf("total expense per month $%d", expense)
}

func main () {
      
  pemp1 := Permanent {
      1, 5000, 20}
  pemp2 := Permanent {
      2, 6000, 30}
  cemp1 := Contract {
      3, 3000}
  employees := []SalaryCalculator{
      pemp1, pemp2, cemp1}
  totalExpense(employees)
}

上面程序的第 7 行声明了一个 SalaryCalculator 接口类型,它只有一个方法 CalculateSalary() int

在公司里,我们有两类员工,即定义的结构体:PermanentContract。长期员工(Permanent)的薪资是 basicpaypf 相加之和,而合同员工(Contract)只有基本工资 basicpay。方法 CalculateSalary 分别实现了以上关系。由于 PermanentContract 都声明了该方法,因此它们都实现了 SalaryCalculator 接口。

声明的 totalExpense 方法体现出了接口的妙用。该方法接收一个 SalaryCalculator 接口的切片([]SalaryCalculator)作为参数。我们向 totalExpense 方法传递了一个包含 PermanentContact 类型的切片。通过调用不同类型对应的 CalculateSalary 方法,totalExpense 可以计算得到支出。

这样做最大的优点是:totalExpense 可以扩展新的员工类型,而不需要修改任何代码。假如公司增加了一种新的员工类型 Freelancer,它有着不同的薪资结构。Freelancer只需传递到 totalExpense 的切片参数中,无需 totalExpense 方法本身进行修改。只要 Freelancer 也实现了 SalaryCalculator 接口,totalExpense 就能够实现其功能。

该程序输出 Total Expense Per Month $14050


我们也可以用指针接受者(Pointer Receiver)实现接口

我们在讨论方法的时候就已经提到过,使用值接受者声明的方法,既可以用值来调用,也能用指针调用。不管是一个值,还是一个可以解引用的指针,调用这样的方法都是合法的。

package main

import "fmt"

type Describer interface {
      
    Describe()
}
type Person struct {
      
    name string
    age  int
}

// 结构体Person使用值接受者,实现了Describer接口
func (p Person) Describe() {
      
    fmt.Printf("%s is %d years oldn", p.name, p.age)
}

type Address struct {
      
    state   string
    country string
}

func (a *Address) Describe() {
       // 使用指针接受者实现
    fmt.Printf("State %s Country %s", a.state, a.country)
}

// p1的类型是Person,在第 29 行,p1赋值给了d1。
// 由于Person实现了接口变量d1,因此在第 30 行,会打印Sam is 25 years old。
func main() {
      
    var d1 Describer // 定义d1是Describer接口变量
    p1 := Person{
      "Sam", 25} // P1类型是Person
    d1 = p1  // p1赋值给了d1
    d1.Describe()  // 由于Person实现了接口变量d1,因此会打印Sam is 25 years old。
    p2 := Person{
      "James", 32}
    d1 = &p2
    d1.Describe() // 与p1同理,值接受者能用指针调用,因此会打印James is 32 years old

    var d2 Describer
    a := Address{
      "Washington", "USA"}

    /* 如果下面一行取消注释会导致编译错误:
       cannot use a (type Address) as type Describer
       in assignment: Address does not implement
       Describer (Describe method has pointer
       receiver)
       为何会报错呢?
       对于使用指针接受者的方法,用一个指针或者一个可取得地址的值来调用都是合法的。
       但接口中存储的具体值(Concrete Value)并不能取到a的地址,于是程序报错。
    */
    //d2 = a

    d2 = &a // 这是合法的
    // 因为在第 22 行,Address 类型的指针实现了 Describer 接口
    d2.Describe()

}

实现多个接口

package main

import (
    "fmt"
)
// 声明接口 SalaryCalculator
type SalaryCalculator interface {
      
    DisplaySalary()
}
// 声明接口 LeaveCalculator
type LeaveCalculator interface {
      
    CalculateLeavesLeft() int
}
// 定义结构体 Employee
type Employee struct {
      
    firstName string
    lastName string
    basicPay int
    pf int
    totalLeaves int
    leavesTaken int
}
// 结构体Employee实现SalaryCalculator接口的DisplaySalary方法
func (e Employee) DisplaySalary() {
      
    fmt.Printf("%s %s has salary $%d", e.firstName, e.lastName, (e.basicPay + e.pf))
}
// 结构体Employee又实现了LeaveCalculator接口的CalculateLeavesLeft方法,于是Employee就实现了SalaryCalculator和LeaveCalculator两个接口。
func (e Employee) CalculateLeavesLeft() int {
      
    return e.totalLeaves - e.leavesTaken
}

func main() {
      
    e := Employee {
      
        firstName: "Naveen",
        lastName: "Ramanathan",
        basicPay: 5000,
        pf: 200,
        totalLeaves: 30,
        leavesTaken: 5,
    }
    var s SalaryCalculator = e // s 是 SalaryCalculator类型的接口变量,e被赋值给s
    s.DisplaySalary()
    var l LeaveCalculator = e // e 的类型 Employee 实现了两种接口,所以是合法的
    fmt.Println("nLeaves left =", l.CalculateLeavesLeft())
}

接口的嵌套

package main

import (
    "fmt"
)

type SalaryCalculator interface {
      
    DisplaySalary()
}

type LeaveCalculator interface {
      
    CalculateLeavesLeft() int
}
// 创建了一个新的接口 EmployeeOperations, 它嵌套了2个接口, 如果一个类型定义了它嵌套的所有接口里面包含的方法(SalaryCalculator,LeaveCalculator),我们就称这类型实现了它(EmployeeOperations)
type EmployeeOperations interface {
      
    SalaryCalculator
    LeaveCalculator
}

type Employee struct {
      
    firstName string
    lastName string
    basicPay int
    pf int
    totalLeaves int
    leavesTaken int
}
// 由于Employee结构体定义了DisplaySalary和CalculateLeavesLeft方法,因此它实现了接口EmployeeOperations。
func (e Employee) DisplaySalary() {
      
    fmt.Printf("%s %s has salary $%d", e.firstName, e.lastName, (e.basicPay + e.pf))
}

func (e Employee) CalculateLeavesLeft() int {
      
    return e.totalLeaves - e.leavesTaken
}

func main() {
      
    e := Employee {
      
        firstName: "Naveen",
        lastName: "Ramanathan",
        basicPay: 5000,
        pf: 200,
        totalLeaves: 30,
        leavesTaken: 5,
    }
    var empOp EmployeeOperations = e // empOp的类型是EmployeeOperations,e的类型是Employee,我们把empOp赋值为e。empOp调用了DisplaySalary()和CalculateLeavesLeft()方法
    empOp.DisplaySalary()
    fmt.Println("nLeaves left =", empOp.CalculateLeavesLeft())
}

参考原文:

『GCTT 出品』Go 系列教程 ——第 19 部分:接口(二)​mp.weixin.qq.com
切片 里面包含interface_Golang - interface 注释笔记_第2张图片
『GCTT 出品』Go 系列教程 ——第 18 部分:接口(一)​mp.weixin.qq.com
切片 里面包含interface_Golang - interface 注释笔记_第3张图片

你可能感兴趣的:(切片,里面包含interface)