分类 Go 下的文章

接口

基础类型 -> struct

type cat struct {...}
type dog struct {...}
func (c cat) eat() {...}
func (d doy) eat() {...}
// 该函数其实是想,传入什么,我就撸什么。如果x的类型是cat,将来只能传递cat类型的变量
// 那dog类型的怎么办?该函数无法支持。struct解决不了这类问题
func lu(x cat) {...}
lu(cat)
lu(dog)  // 无法传入dog类型

struct -> interface

// 引入接口
type eater interface {
    eat()
}
func lu(x eater) {...}
lu(cat)
lu(dog)

接口就是一个特殊类型。它规定了有哪些方法。一个变量实现了接口内的所有方法,就是这个特殊类型的变量。

var d dog
var e eater
// d实例实现了eater的eat(),那么d就是这个接口类型的变量
// 此时d既是struct类型,又是interface类型
e = d

接口变量保存以下 2 个部分:

  • 动态类型:零值nil
  • 动态值:零值nil
fmt.Printf("%T, %v\n", e, e)

这样就能实现接口变量的动态性。可以理解,接口就是一个动态变量。

函数参数:接口

相当于给函数传入实例对象的方法集,能实现方法集内的方法的实例对象,都能执行该函数。

实现接口时:值接收者 VS 指针接收者

  • 值接收者:结构体类型、结构体指针类型的变量都支持
  • 指针接收者:仅支持结构体指针类型的变量

接口嵌套

type sleeper interface {
    sleep()
}
type eater interface {
    eat()
}
type animal interface {
    sleeper
    eater
}

空接口

没有方法,所有类型都实现可以实现空接口。因此 interface{} 通常用来表示任意类型。

// 接口作参数
func show(a interface{}) {
    fmt.Printf("%T, %v", a, a)
}
// 还不知道存入的值是什么类型时
var m = make(map[string]interface{}, 8)

如何判断接口这个动态变量的类型

  • 类型断言
  • 反射

类型断言

x.(type)
// x:    类型为 interface{} 的变量
// type: 断言这个x变量可能的类型
// 返回2个值 v, ok
// 如果ok=true,那么v的类型就是type;如果ok=false,那么v的类型就不是type
func guessType(a interface{}) {
    if s, ok := a.(string); ok {
        fmt.Printf("is a string, value is %v\n", s)
    } else {
        fmt.Printf("unknown type\n")
    }
}
func guessType(a interface{}) {
    switch v := a.(type) {
    case string:
        fmt.Printf("is a string, value is %v\n", v)
    case int:
        fmt.Printf("is a int, value is %v\n", v)    
    case bool:
        fmt.Printf("is a bool, value is %v\n", v)
    default:
        fmt.Printf("unknown type\n")
    }
}

反射

等待学习反射

return 做了什么?

  1. 返回值 赋值
  2. return 返回值

defer 什么时候执行?

  1. 返回值 赋值
  2. defer func
  3. return 返回值

返回值 看作一个中间商,比如一个tmp变量。先回忆一下函数如何定义返回值。

  • func 使用 有名返回值

    func f() x int {
      ...
      return
    }
  • func 使用 匿名返回值

    func f() int {
      ...
      return x
    }

我豁然开朗的关键点

返回值 看作和x、y不一样的另一个变量即可,咱们只关注它的值的指向。


栗子1

func f1() int {
    x := 5
    defer func() {
        x++
    }()
    return x
}
  1. 返回值 = x = 5,返回值指向5
  2. 不管defer如何操作
  3. return 返回值,即 return 5

栗子2

func f2() (x int) {
    defer func() {
        x++
    }()
    return 5
}
  1. 返回值x,x指向5
  2. 闭包defer修改x的指向为6
  3. return 返回值,即 return x,return 6

栗子3

func f3() (y int) {
    x := 5
    defer func() {
        x++
    }()
    return x
}
  1. 返回值y,y=x=5,y指向5
  2. 闭包defer修改x的指向为6
  3. return 返回值,即 return y,return 5

栗子4

func f4() (x int) {
    defer func(x int) {
        x++
    }(x)
    return 5
}
  1. 返回值x,x=5,x指向5
  2. defer值拷贝,内部操作与外部无关
  3. return 返回值,即 return x,return 5