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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1). image 包;Image 接口;

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

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

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

    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.  
    3. import "fmt"
    4.  
    5. func main() {
    6.         c := make(chan int, 2)
    7.         c <- 1
    8.         c <- 2
    9.         // c <- 3 // deadlock !
    10.         fmt.Println(<-c)
    11.         fmt.Println(<-c)
    12. }

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

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

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

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

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

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

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

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


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

本文来自:陆仁贾个人站点

感谢作者:陆仁贾

查看原文:从例子中学习 go 语言 —— 方法、接口以及并发

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

1842 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传