函数、结构体和接口

2020-09-13  本文已影响0人  天命_风流

函数

package main

import "fmt"

type Printer func(contents string) (n int, err error)

func printToStd(contents string) (bytesNum int, err error) {
  return fmt.Println(contents)
}

func main() {
  var p Printer
  p = printToStd
  p("something")
}

编写高阶函数

package main

import (
    "errors"
    "fmt"
)

type operate func(x, y int) int

// 方案1。
func calculate(x int, y int, op operate) (int, error) {
    if op == nil {
        return 0, errors.New("invalid operation")
    }
    return op(x, y), nil
}

// 方案2。
type calculateFunc func(x int, y int) (int, error)

func genCalculator(op operate) calculateFunc {
    return func(x int, y int) (int, error) {
        if op == nil {
            return 0, errors.New("invalid operation")
        }
        return op(x, y), nil
    }
}

func main() {
    // 方案1。
    x, y := 12, 23
    op := func(x, y int) int {
        return x + y
    }
    result, err := calculate(x, y, op)
    fmt.Printf("The result: %d (error: %v)\n",
        result, err)
    result, err = calculate(x, y, nil)
    fmt.Printf("The result: %d (error: %v)\n",
        result, err)

    // 方案2。
    x, y = 56, 78
    add := genCalculator(op)
    result, err = add(x, y)
    fmt.Printf("The result: %d (error: %v)\n",
        result, err)
}

使用高级函数的意义

函数总结

结构体

// AnimalCategory 代表动物分类学中的基本分类法。
type AnimalCategory struct {
  kingdom string // 界。
  phylum string // 门。
  class  string // 纲。
  order  string // 目。
  family string // 科。
  genus  string // 属。
  species string // 种。
}

func (ac AnimalCategory) String() string {
  return fmt.Sprintf("%s%s%s%s%s%s%s",
    ac.kingdom, ac.phylum, ac.class, ac.order,
    ac.family, ac.genus, ac.species)
}

category := AnimalCategory{species: "cat"}
fmt.Printf("The animal category: %s\n", category)  // 打印:The animal category: cat

组合(而非继承)

type Animal struct {
  scientificName string // 学名。
  AnimalCategory    // 动物基本分类。
}
func (a Animal) Category() string {
  return a.AnimalCategory.String()
}
func (a Animal) String() string {
  return fmt.Sprintf("%s (category: %s)",
    a.scientificName, a.AnimalCategory)
}

值方法和指针方法

func (cat *Cat) SetName(name string) {
  cat.name = name
}
  1. 值方法的接收者是该方法所属的那个类型值的一个副本。我们在该方法内对该副本的修改一般都不会体现在原值上,除非这个类型本身是某个引用类型(比如切片或字典)的别名类型。
    而指针方法的接收者,是该方法所属的那个基本类型值的指针值的一个副本。我们在这样的方法内对该副本指向的值进行修改,却一定会体现在原值上。
  2. 一个自定义数据类型的方法集合中仅会包含它的所有值方法,而该类型的指针类型的方法集合却囊括了前者的所有方法,包括所有值方法和所有指针方法。
    严格来讲,我们在这样的基本类型的值上只能调用到它的值方法。但是,Go 语言会适时地为我们进行自动地转译,使得我们在这样的值上也能调用到它的指针方法。
    比如,在Cat类型的变量cat之上,之所以我们可以通过cat.SetName("monster")修改猫的名字,是因为 Go 语言把它自动转译为了(&cat).SetName("monster"),即:先取cat的指针值,然后在该指针值上调用SetName方法。
  3. 在后边你会了解到,一个类型的方法集合中有哪些方法与它能实现哪些接口类型是息息相关的。如果一个基本类型和它的指针类型的方法集合是不同的,那么它们具体实现的接口类型的数量就也会有差异,除非这两个数量都是零。
    比如,一个指针类型实现了某某接口类型,但它的基本类型却不一定能够作为该接口的实现类型。
package main

import "fmt"

type Cat struct {
    name           string // 名字。
    scientificName string // 学名。
    category       string // 动物学基本分类。
}

func New(name, scientificName, category string) Cat {
    return Cat{
        name:           name,
        scientificName: scientificName,
        category:       category,
    }
}

func (cat *Cat) SetName(name string) {
    cat.name = name
}

func (cat Cat) SetNameOfCopy(name string) {
    cat.name = name
}

func (cat Cat) Name() string {
    return cat.name
}

func (cat Cat) ScientificName() string {
    return cat.scientificName
}

func (cat Cat) Category() string {
    return cat.category
}

func (cat Cat) String() string {
    return fmt.Sprintf("%s (category: %s, name: %q)",
        cat.scientificName, cat.category, cat.name)
}

func main() {
    cat := New("little pig", "American Shorthair", "cat")
    cat.SetName("monster") // (&cat).SetName("monster")
    fmt.Printf("The cat: %s\n", cat)

    cat.SetNameOfCopy("little pig")
    fmt.Printf("The cat: %s\n", cat)

    type Pet interface {
        SetName(name string)
        Name() string
        Category() string
        ScientificName() string
    }

    _, ok := interface{}(cat).(Pet)
    fmt.Printf("Cat implements interface Pet: %v\n", ok)
    _, ok = interface{}(&cat).(Pet)
    fmt.Printf("*Cat implements interface Pet: %v\n", ok)
}
------------------
The cat: American Shorthair (category: cat, name: "monster")
The cat: American Shorthair (category: cat, name: "monster")
Cat implements interface Pet: false
*Cat implements interface Pet: true
QianhengdeMacBook-Pro:q3 qianhengtan$ 

接口

package main

import "fmt"

type Pet interface {
    SetName(name string)
    Name() string
    Category() string
}

type Dog struct {
    name string // 名字。
}

func (dog *Dog) SetName(name string) {
    dog.name = name
}

func (dog Dog) Name() string {
    return dog.name
}

func (dog Dog) Category() string {
    return "dog"
}

func main() {
    // 示例1。
    dog := Dog{"little pig"}
    _, ok := interface{}(dog).(Pet)
    fmt.Printf("Dog implements interface Pet: %v\n", ok)  // false
    _, ok = interface{}(&dog).(Pet)
    fmt.Printf("*Dog implements interface Pet: %v\n", ok)  // true
    fmt.Println()

    // 示例2。
    var pet Pet = &dog
    fmt.Printf("This pet is a %s, the name is %q.\n",
        pet.Category(), pet.Name())  // little pig
}

注意,Dog 不是 Pet 的实现类型,因为 Dog 中没有SetName 这个方法(Dog只包含值方法),而 *Dog 包含。

  1. 两个方法的签名完全一致。(参数和返回值的类型和顺序)
  2. 方法名称。

为接口变量赋值会发生什么

    var dog1 *Dog
    fmt.Println("The first dog is nil.")
    dog2 := dog1
    fmt.Println("The second dog is nil.")
    var pet Pet = dog2
    if pet == nil {
        fmt.Println("The pet is nil.")
    } else {
        fmt.Println("The pet is not nil.")  // 这一句会被执行
    }
    fmt.Printf("The type of pet is %T.\n", pet)
    fmt.Printf("The type of pet is %s.\n", reflect.TypeOf(pet).String())
    fmt.Printf("The type of second dog is %T.\n", dog2)
    fmt.Println()
上一篇 下一篇

猜你喜欢

热点阅读