方法
- 定义(方法就是一类带特殊的 接收者 参数的函数。)
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] = ""