A Tour Of Go - 结构体方法

结构体方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
type Animal struct {
mName string
mLegs int
mColor int
}

// value 传值, 复制, 修改不影响原对象
func (animal Animal) Walk() {
fmt.Printf("%v walks with %v legs\n", animal.mName, animal.mLegs);
animal.mColor = 21 // 拷贝复制, 对原对象无效
}

// 引用, 修改影响原对象
func (animal *Animal) Paint(color int) {
animal.mColor = color
}

func Walk (animal Animal) {
fmt.Printf("%v walks with %v legs\n", animal.mName, animal.mLegs);
animal.mColor = 21 // 拷贝复制, 对原对象无效
}

func Paint(animal *Animal, color int) {
animal.mColor = color
}

cat := Animal{"喵喵", 4, 12}
fmt.Println(cat)
cat.Walk()
fmt.Println(cat)
cat.Paint(9)
fmt.Println(cat)

Walk(cat)
fmt.Println(cat)
Paint(&cat, 5)
fmt.Println(cat)

duck := &Animal{"呱呱", 2, 6}
fmt.Println(duck)
duck.Walk()
fmt.Println(duck)
duck.Paint(9)
fmt.Println(duck)

Walk(duck)
fmt.Println(duck)
Paint(duck, 5)
fmt.Println(duck)

函数传参, 值参数必须传值, 指针参数必须传指针

1
2
3
4
5
animal = Animal{}
func f1(x Animal) {} => f(animal)

animal = &Animal{}
func f2(x *Animal) {} => f(animal)

结构体方法传参, 值参数方法也可以传指针, 指针参数方法也可以传值

1
2
3
4
5
6
7
animal = Animal{}
animal.Walk()
animal.Paint(1) => (&animal).Paint(1)

animal = &Animal{}
animal.Walk() => (*animal).Walk()
animal.Paint(1)

如何选择值传递方法和指针传递方法

指针传递可以更改原值, 可以避免在执行方法是进行对象的内存拷贝.

一般情况下均使用指针传值, 并且不推荐两种方法混用.

非结构体方法

必须对普通类型进行重命名

1
2
3
type MyFloat float64

func (f MyFloat) haha() float64 {}

接口结构

接口无需显示声明集成关系, 赋值后的接口存储了(结构的值, 结构指向的指针/值类型),
调用属性方法的时候, 直接使用指向的类型中的方法运行该结构的中的值.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type IAnimal interface {
Walk()
Paint(int)
Talk() string
}

type Duck struct{}

func (duck Duck) Walk() {}
func (duck Duck) Paint(color int) {}
func (duck Duck) Talk() string {
return "I'm Duck"
}

type Cat struct{}

func (cat *Cat) Walk() {}
func (cat *Cat) Paint(color int) {}
func (cat *Cat) Talk() string {
return "I'm Cat"
}

初始化 duck, animal 为是基于值的接口

1
2
3
duck := Duck{}
animal = duck
fmt.Print(animal.Talk())

初始化 cat, animal 为基于引用的接口

1
2
3
cat := &Cat{}
animal = cat
fmt.Print(animal.Talk())

在结构体的方法中, 一般需要手动判断结构体是否为 nil 值

1
2
3
4
5
6
7
func (cat *Cat) Walk () {
if cat == nil {
fmt.Println("Cat <nil>")
return
}
// ...
}

但是将 nil 结构体对象传给接口结构后, 接口结构并非 nil

真正值为 nil 的接口结构

1
2
3
4
type I interface {}

var i I
fmt.Printf("(%v, %T)\n", i, i)

数据类型断言

空接口结构 interface{}, 可以保存任意类型数据, 一般用作处理未知类型数据.

断言 t := i.(T) 如果失败触发 panic

安全断言 t, ok := i.(T) 如果失败 ok=>false, t 为 T 类型的零值

1
2
3
4
5
6
7
switch v:=i.(type) {
case int:
case string:
case bool:
default:
fmt.Printf("Valuie Type Is %T", v)
}

错误处理

错误继承

1
2
3
type error interface {
Error() string
}

调用 Print 的时候打印的是 Error 方法的返回值.

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("connot Sqrt negative number: %v", float64(e))
}

func Sqrt(x float64) (float64, error) {
if x < 0 {
return 0, ErrNegativeSqrt(x)
}

var z, lastValue float64 = 1.0, 1.0

for i := 0; i < 10; i++ {
z -= (z*z - x) / (2 * z)
fmt.Printf("%g %T %v\n", i, z, z)

if z == lastValue {
return z, nil
} else if z < lastValue && lastValue-z < 0.1 {
return z, nil
}
lastValue = z
}
return z, nil
}

常用包 fmt

Errorf(string, interface{}) error

  • Fprint 写io, (int, err)
  • Print 打印 (int, err)
  • Sprint 格式化字符串 (string)

带有 f 结尾为支持标记符, 带有 ln 结尾最后自动加换行.

支持输入 scan

格式化标记:

值: %v, %#v
类型: %T
布尔类型: %t
整型进制: %b %o %d %x/%X %q
浮点: %g %f %m.nf => m 宽度, n 小数位数
字符串: %s %x/%X %q
指针,管道: %p

IO 读写

b := make([]byte, 8)
n, err := io.Read(b)

err == io.EOF 表明读取结束, n 表示读取的字节(只有这些字节是有效的, 其余的是上一次读取的剩余)

定义: package xxx
引入: import “xxx”

Donate - Support to make this site better.
捐助 - 支持我让我做得更好.