go语言学习-方法和接口

314 阅读4分钟

方法

  • 定义(方法就是一类带特殊的 接收者 参数的函数。)
type Example struct {
    name string
}

func (v Example) printName() {
    fmt.Println(v.name)
}
  • 方法只是个带接收者参数的函数。
  • 指针接收者(指针接收者的方法可以修改接收者指向的值,指针为接收者的方法被调用时,接收者既能为值又能为指针)
type Vertex struct {
	X, Y float64
}

func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

接口

  • 接口类型 是由一组方法签名定义的集合。
  • 接口类型的变量可以保存任何实现了这些方法的值。
type Abser interface {
    Abs() float64
}

type Test struct {
    x, y float64
}

func (t *Test) Abs() float64 {
    return math.Sqrt(t.a*t.a + t.b*t.b)
}

func main {
    var a Abser
    t := Test{3, 4}
    a = &t // 假设a = t,那a是无法调用到Test实现的Abs(),因Test的接收者指定是指针
    b := &t
    fmt.Println(a.Abs()) // 结果为5
    fmt.Println(t.Abs()) // 结果为5
    fmt.Println(b.Abs()) // 结果为5
    
}

接口值

类型断言

类型断言 提供了访问接口值底层具体值的方式。

t := i.(T) 该语句断言接口值 i 保存了具体类型 T,并将其底层类型为 T 的值赋予变量 t。

若 i 并未保存 T 类型的值,该语句就会触发一个恐慌。

为了 判断 一个接口值是否保存了一个特定的类型,类型断言可返回两个值:其底层值以及一个报告断言是否成功的布尔值。

t, ok := i.(T) 若 i 保存了一个 T,那么 t 将会是其底层值,而 ok 为 true。

否则,ok 将为 false 而 t 将为 T 类型的零值,程序并不会产生恐慌。

请注意这种语法和读取一个映射时的相同之处。

var i interface{}
i = "hello"

_, ok := i.(string)

if ok{
    fmt.Println("it is string")
}else{
    fmt.Println("it is not string")
}

类型选择

类型选择 是一种按顺序从几个类型断言中选择分支的结构。

类型选择与一般的 switch 语句相似,不过类型选择中的 case 为类型(而非值), 它们针对给定接口值所存储的值的类型进行比较。

switch v := i.(type) {
case T:
    // v 的类型为 T
case S:
    // v 的类型为 S
default:
    // 没有匹配,v 与 i 的类型相同
}

类型选择中的声明与类型断言 i.(T) 的语法相同,只是具体类型 T 被替换成了关键字 type。

此选择语句判断接口值 i 保存的值类型是 T 还是 S。在 T 或 S 的情况下,变量 v 会分别按 T 或 S 类型保存 i 拥有的值。在默认(即没有匹配)的情况下,变量 v 与 i 的接口类型和值相同。

错误

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) error 为 nil 时表示成功;非 nil 的 error 表示失败。

type TestError struct {
    msg string
}
 
func (t *TestError) Error() string{
    return t.msg
}

func run(msg string) error { // error 表示返回为error类型
    return &TestError{  //这里要返回地址,不然打印出来时 `{hello world}`,有个大括号
        msg
    }
}

func main(){
    err :=run("hello world")
    fmt.Println(err)
}

Reader

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

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

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

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

Read 用数据填充给定的字节切片并返回填充的字节数和错误值。在遇到数据流的结尾时,它会返回一个 io.EOF 错误。

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

示例:

func main(){
    r := strings.NewReader("hello world!")
    b := make([]byte, 8) // 长度为8的字节数组
    
    for {
        n, err := r.Read(b) // 每次读取r的8个长度单位,n为实际读取到的长度
        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
		}
    }
}

result:
n = 8 err = <nil> b = [72 101 108 108 111 44 32 82]
b[:n] = "Hello, R"
n = 6 err = <nil> b = [101 97 100 101 114 33 32 82]
b[:n] = "eader!"
n = 0 err = EOF b = [101 97 100 101 114 33 32 82]
b[:n] = ""