Go指南笔记04_(方法和接口)

请注意,本文编写于 641 天前,最后修改于 191 天前,其中某些信息可能已经过时。

Go指南

方法

Go 语言中没有类的概念,但是仍然可以在结构体类型上定义方法. 方法的接受者出现在 func 关键字和方法名之间的参数中.

func (v *T)name() T {}

事实上,我们可以对当前包中出现的任意类型定义任意方法,而不仅限于结构体.但是先决条件是当前包,不能对来自其他包的类型或者基础类型定义方法.

package main

import (
    "fmt"
    "math"
)

type MyFloat float64

func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

func main() {
    f := MyFloat(-math.Sqrt2)
    fmt.Println(f.Abs())
}

理解:上面的代码中,我们定义了MyFloat,MyFloat是64位的float类型,但是MyFloat并不是基本类型,所以我们可以对其定义方法

接收者位指针的方法

方法可以与命名类型或命名类型的指针相关联.我们使用指针类型的的原因有两个:1.避免在每个方法的调用中拷贝值.2.方法可以修改接收者指向的值.

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

将Scale的接受者改为结构体变量v即: func (v Vertex) Scale(f float64) 打印的结果为:
Before scaling: &{X:3 Y:4}, Abs: 5
After scaling: &{X:3 Y:4}, Abs: 5

(未知对错,如有指教,不胜感激)理解:当接受者为指针的时候,我们可以直接通过指针指向的内存区域取值,这样就避免了每一次拷贝值,当不是指针的时候只是进行了值得拷贝然后执行函数中的计算,其实并没有改变结构体本身

接口

接口类型是有一组方法定义的集合,接口类型的值可以存放实现这些方法的任何值.是实现所有方法,而不是只实现一个即可.

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)
}

理解:代码中定义了一个接口类型Abser,同时MyFloat类型和Vertex的指针类型实现了Abser中定义的方法,所以可以使用接口类型Abser变量v调用其方法Abs(),而值类型v没有实现Abs()方法,所以其不满足Abser

隐式接口

类型通过实现接口中的方法来实现接口.没有显式声明的必要,所以也就没有implement关键字.

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

错误

Go程序使用error值来表示错误状态,其值不为nil的时候表示出现了错误.error类型是一个内建接口.

Readers

io包中制定了io.Readers接口,接口中的Read方法func (T) Read(b []byte) (n int, err error)可以实现用数据填充指定字节的slice,并且返回填充的字节数和错误信息,当遇到数据流结尾是,返回io.EOF错误.

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
        }
    }
}

上面代码以每次8字节的读取速度读取"Hello, Reader!"字符串,当返回的err错误信息为io.EOF的时候表征读取结束.

Comments

添加新评论