方法和接口
方法
方法可以当作带接收者参数的函数
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()来保证一定会被解锁