分享
  1. 首页
  2. 文章

从例子中学习 go 语言 —— 方法、接口以及并发

陆仁贾 · · 2807 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

本篇本来是接着上一篇的,本篇是 go 的关于方法、接口以及并发的一些例子;上一篇是关于数据结构以及指针的,上上一篇是关于基本语法以及结构的,基本类型、函数、for、if 等等。

  1. 方法:
    1. package main
    2. import (
    3. "fmt"
    4. "math"
    5. )
    6. type Vertex struct {
    7. X, Y float64
    8. }
    9. func (v *Vertex) Abs() float64 {
    10. return math.Sqrt(v.X*v.X + v.Y*v.Y)
    11. }
    12. func main() {
    13. v := &Vertex{3, 4}
    14. fmt.Println(v.Abs())
    15. //fmt.Println((*v).Abs()) // ok,2
    16. }

    1). 所谓的"方法"(method) —— 就是特殊的函数,只不过与某个类型绑定起来了而已,比如 Abs 绑定到了 *Vertex 类型; 2). Abs 绑定到了 *Vertex 上之后,就可以使用 *Vertex 值去调用这个方法,比如 v.Abs() ; 3). Abs 绑定到了 *Vertex 上之后,函数体内部可以使用 *Vertex 可以访问到的东西,比如 v.X ;

  2. 在基本类型上定义方法:
    1. package main
    2. import (
    3. "fmt"
    4. "math"
    5. )
    6. type MyFloat float64
    7. func (f MyFloat) Abs() float64 {
    8. if f < 0 {
    9. return float64(-f)
    10. }
    11. return float64(f)
    12. }
    13. func main() {
    14. f := MyFloat(-math.Sqrt2)
    15. fmt.Println(f.Abs())
    16. }

    1). 实际上不能在基本类型,比如 int 上定义方法,但是简单重重命名一下,便可以了; 2). 可以在自己包内的类型上定义方法,不能对包含的其他包和基本类型定义方法; 3). 使用 float64 定义 MyFloat,可以将 float64 直接传给 MyFLoat,但是不能反过来直接传递,可以 float64(f) ;

  3. 在指针上定义方法:
    1. package main
    2. import (
    3. "fmt"
    4. "math"
    5. )
    6. type Vertex struct {
    7. X, Y float64
    8. }
    9. func (v *Vertex) Scale(f float64) {
    10. v.X = v.X * f
    11. v.Y = v.Y * f
    12. }
    13. func (v *Vertex) Abs() float64 {
    14. return math.Sqrt(v.X*v.X + v.Y*v.Y)
    15. }
    16. func main() {
    17. v := &Vertex{3, 4}
    18. v.Scale(5)
    19. fmt.Println(v, v.Abs())
    20. }

    1). 使用指针定义方法,一是为了避免大量数据拷贝;而是可以通过指针修改域外的东西;

  4. 接口:
    1. package main
    2. import (
    3. "fmt"
    4. "math"
    5. )
    6. type Abser interface {
    7. Abs() float64
    8. }
    9. func main() {
    10. var a Abser
    11. f := MyFloat(-math.Sqrt2)
    12. v := Vertex{3, 4}
    13. a = f // a MyFloat implements Abser
    14. fmt.Println(a.Abs())
    15. a = &v // a *Vertex implements Abser
    16. //a = v // a Vertex, does NOT
    17. // implement Abser
    18. fmt.Println(a.Abs())
    19. }
    20. type MyFloat float64
    21. func (f MyFloat) Abs() float64 {
    22. if f < 0 {
    23. return float64(-f)
    24. }
    25. return float64(f)
    26. }
    27. type Vertex struct {
    28. X, Y float64
    29. }
    30. func (v *Vertex) Abs() float64 {
    31. return math.Sqrt(v.X*v.X + v.Y*v.Y)
    32. }

    1). 接口是一种类型,里面装的是一系列方法声明,也就是多个方法声明; 2). 一个接口值可以用任一个实现了该接口的实例/对象赋值; 3). 实现接口的如果是指针类型 *T,那么,只能使用 *T 变量赋值给接口,而不能是 T 。

  5. 接口继承:
    1. package main
    2. import (
    3. "fmt"
    4. "os"
    5. )
    6. type Reader interface {
    7. Read(b []byte) (n int, err error)
    8. }
    9. type Writer interface {
    10. Write(b []byte) (n int, err error)
    11. }
    12. type ReadWriter interface {
    13. Reader
    14. Writer
    15. }
    16. func main() {
    17. var w Writer
    18. // os.Stdout implements Writer
    19. w = os.Stdout
    20. fmt.Fprintf(w, "hello, writer\n")
    21. }

    1). 接口继承; 2). os 包;os.Stdout ; fmt.Fprintf ;标准输出流、流格式化输出;os.Stdout 实现了 Writer 接口,可以使用 fmt.Fprintf 向 os.Stdout 写内容;

  6. error 错误处理:
    1. package main
    2. import (
    3. "fmt"
    4. "time"
    5. )
    6. type MyError struct {
    7. When time.Time
    8. What string
    9. }
    10. func (e *MyError) Error() string {
    11. return fmt.Sprintf("at %v, %s",
    12. e.When, e.What)
    13. }
    14. func run() error {
    15. return &MyError{
    16. time.Now(),
    17. "it didn't work",
    18. }
    19. }
    20. func main() {
    21. if err := run(); err != nil {
    22. fmt.Println(err)
    23. }
    24. }

    1). error 是一个接口; 2). fmt.Sprintf 格式化字符串输出; 3). 只要是能够用 Error 函数描述自己错误信息的类型,都可以作为 error ;

    type error interface {
    	Error() string
    }
    
  7. web server —— web服务器:
    1. package main
    2. import (
    3. "fmt"
    4. "net/http"
    5. )
    6. type Hello struct{}
    7. func (h Hello) ServeHTTP(
    8. w http.ResponseWriter,
    9. r *http.Request ) {
    10. fmt.Fprintf(w, "Hello")
    11. }
    12. func main() {
    13. var h Hello
    14. http.ListenAndServe("localhost:4000",h)
    15. }

    1). net/http 包; 2). http 包使用 Handler 接口处理请求;

    package http
    type Handler interface {
    	ServeHTTP(w ResponseWriter, r *Request)
    }
    
  8. image 处理:
    1. package main
    2. import (
    3. "fmt"
    4. "image"
    5. )
    6. func main() {
    7. m := image.NewRGBA(image.Rect(0, 0, 100, 100))
    8. fmt.Println(m.Bounds())
    9. fmt.Println(m.At(0, 0).RGBA())
    10. }

    1). image 包;Image 接口;

    package image
    type Image interface {
    	ColorModel() color.Model
    	Bounds() Rectangle
    	At(x, y int) color.Color
    }
    
  9. 并发 / 多线程:
    1. package main
    2. import (
    3. "fmt"
    4. "runtime"
    5. )
    6. func say(s string) {
    7. for i := 0; i < 5; i++ {
    8. runtime.Gosched()
    9. fmt.Println(s)
    10. }
    11. }
    12. func main() {
    13. go say("world")
    14. say("hello")
    15. }

    1). 并发、多线程;一个 goroutine 是个轻量级线程; 2). 这个程序赶脚也有点诡异!

  10. 通道 channels:
    1. package main
    2. import "fmt"
    3. func sum(a []int, c chan int) {
    4. sum := 0
    5. for _, v := range a {
    6. sum += v
    7. }
    8. c <- sum // send sum to c
    9. }
    10. func main() {
    11. a := []int{7, 2, 8, -9, 4, 0}
    12. c := make(chan int)
    13. //c := make(chan int,10)
    14. go sum(a[:len(a)/2], c)
    15. go sum(a[len(a)/2:], c)
    16. x, y := <-c, <-c // receive from c
    17. fmt.Println(x, y, x + y)
    18. }

    1). 通道;有点管道 pipeline 的味道,一端写东西,从另外一端读东西; 2). 默认具有同步操作; 3). channel 使用前必须 make 一下,带不带第二个参数结果情况是有区别的;

    ch := make(chan int)
    ch <- v // Send v to channel ch. v := <-ch // Receive from ch, and assign value to v. 
  11. 带缓冲 buffered 通道 channels:
    1. package main
    2. import "fmt"
    3. func main() {
    4. c := make(chan int, 2)
    5. c <- 1
    6. c <- 2
    7. // c <- 3 // deadlock !
    8. fmt.Println(<-c)
    9. fmt.Println(<-c)
    10. }

    1). 带缓冲通道;full 阻塞写;empty 阻塞读; 2). 带缓冲和不带缓冲情况是有差异的,不仅仅是效率的问题;

  12. 主动关闭通道:
    1. package main
    2. import (
    3. "fmt"
    4. )
    5. func fibonacci(n int, c chan int) {
    6. x, y := 0, 1
    7. for i := 0; i < n; i++ {
    8. c <- x
    9. x, y = y, x + y
    10. }
    11. close(c)
    12. }
    13. func main() {
    14. c := make(chan int, 10)
    15. go fibonacci(cap(c), c)
    16. for i := range c {
    17. fmt.Println(i)
    18. }
    19. }

    1). 发送者可以主动关闭通道,只是在需要的地方,比如为了结束 range 循环;

  13. select:
    1. package main
    2. import "fmt"
    3. func fibonacci(c, quit chan int) {
    4. x, y := 0, 1
    5. for {
    6. select {
    7. case c <- x:
    8. x, y = y, x + y
    9. case <- quit:
    10. fmt.Println("quit")
    11. return
    12. }
    13. }
    14. }
    15. func main() {
    16. c := make(chan int)
    17. quit := make(chan int)
    18. go func() {
    19. for i := 0; i < 10; i++ {
    20. fmt.Println(<-c)
    21. }
    22. quit <- 0
    23. }()
    24. fibonacci(c, quit)
    25. }

    1). select 只要有一个 case 可以执行便执行,否则便阻塞;例子实现了同步操作; 2). 如果有多个可以选择,select 是随机选择一个执行;

  14. 带默认 default 的select:
    1. package main
    2. import (
    3. "fmt"
    4. "time"
    5. )
    6. func main() {
    7. tick := time.Tick(1e8)
    8. boom := time.After(5e8)
    9. for {
    10. select {
    11. case <-tick:
    12. fmt.Println("tick.")
    13. case <-boom:
    14. fmt.Println("BOOM!")
    15. return
    16. default:
    17. fmt.Println(" .")
    18. time.Sleep(5e7)
    19. }
    20. }
    21. }

    1). select default 选择没有可以执行的 case 情况下的默认动作;

由于篇幅所限,也为了看着方便,前面的见上一篇文章


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
2807 次点击
1 回复 | 直到 2025年05月08日 07:58:09
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏