go语言基础(二)|青训营笔记

65 阅读3分钟

方法和接口

方法

方法可以当作带接收者参数的函数

type Vertex struct {
    X, Y float64
}

//接收者参数位于 func 关键字和方法名之间
func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X * v.X + v.Y * v.Y)
}

接收者参数可以分为值接收者和指针接收者,对于值接收者,方法只会对原始值的副本进行操作

type Vertex struct {
    X, Y float64
}

//不会修改原始值
func (v Vertex) Scale1(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

//会修改原始值
func (v *Vertex) Scale2(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

注:当对象为nil时,go可以通过方法优雅的处理这种情况

type Vertex struct {
	X, Y float64
}

func (v *Vertex) M() {
	if v == nil {
		fmt.Println("<nil>")
		return
	}
	fmt.Println(v)
}

func main() {
	var v *Vertex
	v.M() //打印<nil>
}

接口

接口类型是由一组方法签名定义的集合,其变量可以保存任何实现了这些方法的值(故无需使用implements关键字显式声明)

type Abser interface {
	Abs() float64
}

type MyFloat float64

func (f MyFloat) Abs() float64 {
	if f < 0 {
		return float64(-f)
	}
	return float64(f)
}

func main() {
    var a Abser
    a = MyFloat(-math.Sqrt2)
    fmt.Println(a.Abs())
}

在内部,接口值可以看做包含值和具体类型的元组:(value, type)

接口为nil的值既不保存值也不保存具体类型,在调用方法是会产生运行错误

type I interface {
	M()
}

func main() {
	var i I
	describe(i)
	i.M()
}

func describe(i I) {
	fmt.Printf("(%v, %T)\n", i, i)
}
/*打印
(<nil>, <nil>)
panic: runtime error: invalid memory address or nil pointer dereference
*/

指定了零个方法的接口值被称为空接口:interface{},空接口可保存任何类型的值

类型断言提供了访问接口值底层具体值的方式,有两种断言方式

type Vertex struct {
	X, Y float64
}

func main() {
	var i interface{} = Vertex{1, 2}
    //当接口未保存此类型的值,会触发恐慌
    s := i.(Vertex)
	fmt.Println(s)

    //用这种方式来判断是否保存了特定类型
	s, ok := i.(float64)
	if ok {
		fmt.Println(s)
		return
	}
}

并发

Go程

Go运行时管理的轻量级线程

//f,x,y和z的求值发生在当前的Go程中,而f的执行发生在新的Go程中
func f(x, y, z int) {
	//...
}

func main() {
	go f(1, 2, 3)
}

线程通讯/同步

信道

信道是带有类型的管道,你可以通过它用信道操作符<-来发送或者接收值

//信道的创建
ch := make(chan int)
ch2 := make(chan int, 5) //带缓冲的信道,仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞

//信道的发送
sum := 100
ch <- sum
//信道的接收
s1 := <-ch
for i := range ch {
	//...
}

发送者可通过close关闭信道,表示没有需要发送的值

ch := make(chan int, 10)
for i := 0; i < 10; i++ {
	ch <- i
}
close(ch)

通过range可以不断从信道中接收值,直到信道无可接收数据且被关闭

//接上文
//close(ch)
for i := range ch {
	fmt.Println(i)
}

注:信道与文件不同,通常情况下无需关闭它们。只有在必须告诉接收者不再有需要发送的值时才有必要关闭

当需要从多个通讯中仅等待一个通讯的执行完才能进行后续操作时,可以用select语句

func fibonacci(c, quit chan int) {
	x, y := 0, 1
	for {
		select {
		//三个case一趟循环只能执行其中一个
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("quit")
			return
		default: //其他分支都没准备好,就执行这个
			fmt.Println("default")
			return
		}
	}
}

sync.Mutex

go标准库中实现互斥概念的数据结构为互斥锁(mutex)

var mux sync.Mutex

mux.Lock()
//...
mux.UnLock() //可以用 defer mux.UnLock()来保证一定会被解锁