Go 入门 - 方法和接口

方法和接口

方法的接受者

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 Abs(v Vertex) float64 { //正常的函数也可以这样写,并且不会和上一个函数重载
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
	v := Vertex{3, 4}
	fmt.Println(v.Abs()) // output 5
    fmt.Println(Abs(v)) // output 5
}

注意,接受者只能是同一包内定义的类型,不能使其他包内定义的类型或者内建类型。如果想要为float64声明一个接受者,那也只能重新定义一个类型type MyFloat float64

指针接受者

接受者可以是指向某类结构体的指针。如果是指针,那么在就可以修改结构体内部的变量。

当指针为接受者的方法被调用时,接受者既能为值也能为指针。同样,当以值为接受者的方法被调用的时候,接受者技能为值又能为指针。在这个一一下,接受者是值还是指针本质上区别是方法是否可以修改结构体的变量。

接口

接口定义了一个方法的集合。不同的结构体通过至少实现这些方法可以生成不同类型的接口。接口值可以看做包含值和具体类型的元组。

package main

import "fmt"

type I interface {
	M()
}

type T struct {
	S string
}

func (t *T) M() {
	if t == nil {
		fmt.Println("<nil>")
		return
	}
	fmt.Println(t.S)
}

func main() {
	var i I

	var t *T
	i = t // 类型赋予接口,需要注意的是 nil值的接口的方法也会被调用
	describe(i)
	i.M()

	i = &T{"hello"}
	describe(i)
	i.M()
}

func describe(i I) {
	fmt.Printf("(%v, %T)\n", i, i)
}

指定了零个方法的接口被称为空接口interface{}是空接口的类型,写在函数的参数表里面也是这样写的

package main

import "fmt"

func main() {
	var i interface{}
	describe(i)

	i = 42
	describe(i)

	i = "hello"
	describe(i)
}

func describe(i interface{}) {
	fmt.Printf("(%v, %T)\n", i, i)
}

类型断言

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

t := i.(T)

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

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

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

t, ok := i.(T)

i 保存了一个 T,那么 t 将会是其底层值,而 oktrue

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

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

package main

import "fmt"

func main() {
	var i interface{} = "hello"

	s := i.(string)
	fmt.Println(s)

	s, ok := i.(string)
	fmt.Println(s, ok)

	f, ok := i.(float64)
	fmt.Println(f, ok)

	f = i.(float64) // panic
	fmt.Println(f)
}

类型选择

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

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

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

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

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

package main

import "fmt"

func do(i interface{}) {
	switch v := i.(type) {
	case int:
		fmt.Printf("Twice %v is %v\n", v, v*2)
	case string:
		fmt.Printf("%q is %v bytes long\n", v, len(v))
	default:
		fmt.Printf("I don't know about type %T!\n", v)
	}
}

func main() {
	do(21)
	do("hello")
	do(true)
}
posted @ 2018-11-08 10:04  JHaoWon  阅读(127)  评论(0编辑  收藏  举报