美文网首页程序员
Go 指南 方法和接口

Go 指南 方法和接口

作者: liycode | 来源:发表于2017-03-12 17:41 被阅读70次

    一、方法

    Go 没有类。然而,仍然可以在结构体类型上定义方法。

    方法接收者 出现在 func 关键字和方法名之间的参数中。

    package main
    
    import (
        "fmt"
        "math"
    )
    
    type Vertex struct {
        X, Y float64
    }
    
    func (v *Vertex) Abs() float64 {
        return math.Sqrt(v.X*v.X + v.Y*v.Y)
    }
    
    func main() {
        v := &Vertex{3, 4}
        fmt.Println(v.Abs())
    }
    

    你可以对包中的 任意 类型定义任意方法,而不仅仅是针对结构体。

    但是,不能对来自其他包的类型或基础类型定义方法。

    方法可以与命名类型或命名类型的指针关联。

    刚刚看到的两个 Abs 方法。一个是在 *Vertex 指针类型上,而另一个在 MyFloat 值类型上。 有两个原因需要使用指针接收者。首先避免在每个方法调用中拷贝值(如果值类型是大的结构体的话会更有效率)。其次,方法可以修改接收者指向的值。

    尝试修改 Abs 的定义,同时 Scale 方法使用 Vertex 代替 *Vertex 作为接收者。

    当 v 是 Vertex 的时候 Scale 方法没有任何作用。Scale 修改 v。当 v 是一个值(非指针),方法看到的是 Vertex的副本,并且无法修改原始值。

    Abs 的工作方式是一样的。只不过,仅仅读取 v。所以读取的是原始值(通过指针)还是那个值的副本并没有关系。

    package main
    
    import (
        "fmt"
        "math"
    )
    
    type Vertex struct {
        X, Y float64
    }
    
    func (v *Vertex) Scale(f float64) {
        v.X = v.X * f
        v.Y = v.Y * f
    }
    
    func (v *Vertex) Abs() float64 {
        return math.Sqrt(v.X*v.X + v.Y*v.Y)
    }
    
    func main() {
        v := &Vertex{3, 4}
        fmt.Printf("Before scaling: %+v, Abs: %v\n", v, v.Abs())
        v.Scale(5)
        fmt.Printf("After scaling: %+v, Abs: %v\n", v, v.Abs())
    }
    

    输出结果为:
    Before scaling: &{X:3 Y:4}, Abs: 5
    After scaling: &{X:15 Y:20}, Abs: 25

    二、接口

    接口类型是由一组方法定义的集合。

    接口类型的值可以存放实现这些方法的任何值。

    注意: 示例代码的 22 行存在一个错误。 由于 Abs 只定义在 *Vertex(指针类型)上, 所以 Vertex(值类型)不满足 Abser

    package main
    
    import (
        "fmt"
        "math"
    )
    
    type Abser interface {
        Abs() float64
    }
    
    func main() {
        var a Abser
        f := MyFloat(-math.Sqrt2)
        v := Vertex{3, 4}
    
        a = f  // a MyFloat 实现了 Abser
        a = &v // a *Vertex 实现了 Abser
    
        // 下面一行,v 是一个 Vertex(而不是 *Vertex)
        // 所以没有实现 Abser。
        a = v
    
        fmt.Println(a.Abs())
    }
    
    type MyFloat float64
    
    func (f MyFloat) Abs() float64 {
        if f < 0 {
            return float64(-f)
        }
        return float64(f)
    }
    
    type Vertex struct {
        X, Y float64
    }
    
    func (v *Vertex) Abs() float64 {
        return math.Sqrt(v.X*v.X + v.Y*v.Y)
    }
    

    2.1 隐式接口

    类型通过实现那些方法来实现接口。 没有显式声明的必要;所以也就没有关键字implements

    隐式接口解耦了实现接口的包和定义接口的包:互不依赖。

    因此,也就无需在每一个实现上增加新的接口名称,这样同时也鼓励了明确的接口定义。

    包 io 定义了 ReaderWriter;其实不一定要这么做。

    package main
    
    import (
        "fmt"
        "os"
    )
    
    type Reader interface {
        Read(b []byte) (n int, err error)
    }
    
    type Writer interface {
        Write(b []byte) (n int, err error)
    }
    
    type ReadWriter interface {
        Reader
        Writer
    }
    
    func main() {
        var w Writer
    
        // os.Stdout 实现了 Writer
        w = os.Stdout
    
        fmt.Fprintf(w, "hello, writer\n")
    }
    

    2.2 Stringers

    一个普遍存在的接口是 fmt 包中定义的 Stringer。

    type Stringer interface {
        String() string
    }
    

    Stringer 是一个可以用字符串描述自己的类型。fmt包 (还有许多其他包)使用这个来进行输出。类似Java中的toString()

    package main
    
    import "fmt"
    
    type Person struct {
        Name string
        Age  int
    }
    
    func (p Person) String() string {
        return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
    }
    
    func main() {
        a := Person{"Arthur Dent", 42}
        z := Person{"Zaphod Beeblebrox", 9001}
        fmt.Println(a, z)
    }
    

    2.3 错误

    Go 程序使用 error 值来表示错误状态。

    fmt.Stringer 类似, error 类型是一个内建接口:

    type error interface {
        Error() string
    }
    

    与 fmt.Stringer 类似,fmt 包在输出时也会试图匹配 error。

    通常函数会返回一个 error 值,调用的它的代码应当判断这个错误是否等于 nil, 来进行错误处理。

    i, err := strconv.Atoi("42")
    if err != nil {
        fmt.Printf("couldn't convert number: %v\n", err)
        return
    }
    fmt.Println("Converted integer:", i)
    

    errornil 时表示成功;非 nilerror 表示错误。

    package main
    
    import (
        "fmt"
        "time"
    )
    
    type MyError struct {
        When time.Time
        What string
    }
    
    func (e *MyError) Error() string {
        return fmt.Sprintf("at %v, %s",
            e.When, e.What)
    }
    
    func run() error {
        return &MyError{
            time.Now(),
            "it didn't work",
        }
    }
    
    func main() {
        if err := run(); err != nil {
            fmt.Println(err)
        }
    }
    

    2.4 Readers

    io 包指定了 io.Reader 接口, 它表示从数据流结尾读取。

    Go 标准库包含了这个接口的许多实现, 包括文件、网络连接、压缩、加密等等。

    io.Reader 接口有一个 Read 方法:

    func (T) Read(b []byte) (n int, err error)
    

    Read 用数据填充指定的字节 slice,并且返回填充的字节数和错误信息。 在遇到数据流结尾时,返回 io.EOF 错误。

    例子代码创建了一个 strings.Reader。 并且以每次 8 字节的速度读取它的输出。

    package main
    
    import (
        "fmt"
        "io"
        "strings"
    )
    
    func main() {
        r := strings.NewReader("Hello, Reader!")
    
        b := make([]byte, 8)
        for {
            n, err := r.Read(b)
            fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
            fmt.Printf("b[:n] = %q\n", b[:n])
            if err == io.EOF {
                break
            }
        }
    }
    

    三、Web 服务器

    http 通过任何实现了 http.Handler 的值来响应 HTTP 请求:

    package http
    
    type Handler interface {
        ServeHTTP(w ResponseWriter, r *Request)
    }
    

    在这个例子中,类型 Hello 实现了 http.Handler。

    访问 http://localhost:4000/ 会看到来自程序的问候。

    package main
    
    import (
        "fmt"
        "log"
        "net/http"
    )
    
    type Hello struct{}
    
    func (h Hello) ServeHTTP(
        w http.ResponseWriter,
        r *http.Request) {
        fmt.Fprint(w, "Hello!")
    }
    
    func main() {
        var h Hello
        err := http.ListenAndServe("localhost:4000", h)
        if err != nil {
            log.Fatal(err)
        }
    }
    

    四、图片

    Package image 定义了 Image 接口:

    package image
    
    type Image interface {
        ColorModel() color.Model
        Bounds() Rectangle
        At(x, y int) color.Color
    }
    

    注意:Bounds 方法的 Rectangle 返回值实际上是一个 image.Rectangle, 其定义在 image 包中。

    (具体可以参阅文档了解全部信息。)

    color.Colorcolor.Model 也是接口,但是通常因为直接使用预定义的实现 image.RGBAimage.RGBAModel 而被忽视了。这些接口和类型由image/color 包定义。

    package main
    
    import (
        "fmt"
        "image"
    )
    
    func main() {
        m := image.NewRGBA(image.Rect(0, 0, 100, 100))
        fmt.Println(m.Bounds())
        fmt.Println(m.At(0, 0).RGBA())
    }
    

    相关文章

      网友评论

        本文标题:Go 指南 方法和接口

        本文链接:https://www.haomeiwen.com/subject/gtqxnttx.html