方法和接口
方法
Go中没有类,但是可以为结构体定义方法,方法就是一类带有特殊的接受者参数的函数。
方法接受者位于func关键字和方法名之间。
可以为非结构体类型声明方法,但不能为其它包内定义的类型的接收者声明方法,且不能为内建类型声明方法。
// 函数
package main
import "fmt"
func add(x int, y int) int {
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
// 方法
package main
import "fmt"
type rect struct {
width, height int
}
func (r *rect) area() int {
return r.width * r.height
}
func (r rect) perim() int {
return 2*r.width + 2*r.height
}
func main() {
r := rect{width: 10, height: 5}
fmt.Println("area: ", r.area())
fmt.Println("perim:", r.perim())
rp := &r
fmt.Println("area: ", rp.area())
fmt.Println("perim:", rp.perim())
}
// output
area: 50
perim: 30
area: 50
perim: 30
函数是完全闭包的
package main
import "fmt"
// 函数 adder 返回一个闭包。每个闭包被绑定到自己的 sum 变量上
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
接口
接口类型是由一组方法签名的集合。
接口类型的值可以保存任何实现了接口方法的变量。
类型通过实现了一个接口的所有方法来实现这个接口,而不需要专门的显示声明也就是”implements”关键字来声明。
package main
import "fmt"
import "math"
type geometry interface {
area() float64
perim() float64
}
type rect struct {
width, height float64
}
type circle struct {
radius float64
}
func (r rect) area() float64 {
return r.width * r.height
}
func (r rect) perim() float64 {
return 2*r.width + 2*r.height
}
func (c circle) area() float64 {
return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
return 2 * math.Pi * c.radius
}
func measure(g geometry) {
fmt.Println(g)
fmt.Println(g.area())
fmt.Println(g.perim())
}
func main() {
r := rect{width: 3, height: 4}
c := circle{radius: 5}
measure(r)
measure(c)
}
// output
{3 4}
12
14
{5}
78.53981633974483
31.41592653589793
error
Go编程提供了一个非常简单的错误处理框架,以及内置的错误接口类型,如下声明:
type error interface {
Error() string
}
Go函数通常返回错误作为最后一个返回值。 可使用errors.New来构造一个基本的错误消息
package main
import "errors"
import "fmt"
import "math"
func Sqrt(value float64)(float64, error) {
if(value < 0){
return 0, errors.New("Math: negative number passed to Sqrt")
}
return math.Sqrt(value), nil
}
func main() {
result, err:= Sqrt(-1)
if err != nil {
fmt.Println(err)
}else {
fmt.Println(result)
}
result, err = Sqrt(9)
if err != nil {
fmt.Println(err)
}else {
fmt.Println(result)
}
}
并发
goroutine
goroutine 是由 Go 运行时环境管理的轻量级线程。
使用 go f(x, y, z) 开启一个新的 goroutine 执行。
package main
import "fmt"
func f(from string) {
for i := 0; i < 3; i++ {
fmt.Println(from, ":", i)
}
}
func main() {
f("direct")
go f("goroutine")
go func(msg string) {
fmt.Println(msg)
}("going")
fmt.Println("done")
}
// output
$ go run goroutines.go
direct : 0
direct : 1
direct : 2
goroutine : 0
going
goroutine : 1
goroutine : 2
done
channel
channel 是有类型的管道,可以用 channel 操作符 <- 对其发送或者接收值。
ch <- v // 将 v 送入 channel ch。
v := <-ch // 从 ch 接收,并且赋值给 v。
默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得 goroutine 可以在没有明确的锁或竞态变量的情况下进行同步。
package main
import "fmt"
func main() {
messages := make(chan string)
go func() { messages <- "ping" }()
msg := <-messages
fmt.Println(msg)
}
channel 可以是带缓冲的。为 make 提供第二个参数作为缓冲长度来初始化一个缓冲 channel:
ch := make(chan int, 100)
向缓冲 channel 发送数据的时候,只有在缓冲区满的时候才会阻塞。当缓冲区清空的时候接受阻塞。
package main
import "fmt"
func main() {
c := make(chan int, 2)
c <- 1
c <- 2
fmt.Println(<-c)
fmt.Println(<-c)
}
close
发送者可以 close 一个 channel 来表示再没有值会被发送了。接收者可以通过赋值语句的第二参数来测试 channel 是否被关闭:当没有值可以接收并且 channel 已经被关闭,那么
v, ok := <-ch
ok 会被设置为 false。
注意: 只有发送者才能关闭 channel,而不是接收者。向一个已经关闭的 channel 发送数据会引起 panic。
package main
import "fmt"
func main() {
jobs := make(chan int, 5)
done := make(chan bool)
go func() {
for {
j, more := <-jobs
if more {
fmt.Println("received job", j)
} else {
fmt.Println("received all jobs")
done <- true
return
}
}
}()
for j := 1; j <= 3; j++ {
jobs <- j
fmt.Println("sent job", j)
}
close(jobs)
fmt.Println("sent all jobs")
<-done
}
select
Go语言的选择(select)可等待多个通道操作。将goroutine和channel与select结合是Go语言的一个强大功能。
select 会阻塞,直到条件分支中的某个可以继续执行,这时就会执行那个条件分支。如果有多个都准备好的时候,会随机选一个。
package main
import "time"
import "fmt"
func main() {
c1 := make(chan string)
c2 := make(chan string)
c3 := make(chan string)
t1 := time.Now().UnixNano()
go func() {
time.Sleep(time.Second * 1)
c1 <- "one"
}()
go func() {
time.Sleep(time.Second * 2)
c2 <- "two"
}()
go func() {
time.Sleep(time.Second * 2)
c3 <- "three"
}()
for i := 0; i < 3; i++ {
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
case msg3 := <-c3:
fmt.Println("received", msg3)
}
}
t2 := time.Now().UnixNano()
dt := (t2 - t1) / 1e6
fmt.Println(dt)
}