3. Struct 与 面向对象 |《 刻意学习 Golang 》

go 的变量是 name type, 老是写成 c 的 type name go 的 type name struct c 的typedef struct name

struct

对比 PHP: PHP 中没有 struct 类型,这里对比 C 的 struct

  • 支持匿名字段「嵌入字段」
  • type = c 的 typedef
  • 初始化有所区别
  • 声明
  • 初始化
  • 访问

先看一下 C 中的 struct

// 结构体声明常用的两种
// 通过 typedef
typedef struct Human {
    char* name;
    int age;
} *pHuman;
// 直接声明
struct Human {
    char* name;
    int age;
};
struct Staff {
    Human* human; // 指向结构体的指针
    Human human; // 包含结构体
    pHuman human; // 包含指向结构体的指针
    int wage;
}

再看 Go

type Human struct {
    name string
    age int
}
var tom Human
// 通过赋值初始化
tom.name, tom.age = "Tom", 18
// 详细初始化
jerry := Human{age:25, name:"Jerry"}
// 按照结构体声明顺序
peter := Human{"Peter", 34}
// 通过 . 访问
fmt.Printf("%s is %d old\n", tom.name, tom.age)

匿名字段

匿名字段也叫「嵌入字段」顾名思义,嵌入到一个结构体的字段

只提供类型,而不写字段名。『通过这个来定义』

  • 支持:类型、自定类型、结构体
  • 如果是结构体:隐式的引入这个结构体的字段到新结构体
  • 重复字段最外层优先原则,里层通过 隐式类型访问
type Staff struct {
    Human // 隐式的引入 Human 的字段
    wage int
    age float32 // 覆盖 Human 的 age
}
tom := Staff{Human{"Tom", 18}, 1000, 18.5}
jerr := Staff{Human:Human{age:32, name:"Jerr"}, wage: 2000, age: 32.5}
fmt.Printf("%s is %f old,%d years, wage is %d", tom.name, tom.Human.age, tom.age, tom.wage)
fmt.Printf("%s is %f old,%d years, wage is %d", jerr.name, jerr.Human.age, jerr.age, jerr.wage)

面向对象

看完了 go 的面相对象后,emmmm… 感觉有点颠覆。首先是告别 class 。 然后一系列的 static public protected private 都没有了,甚至是 extends parent 都省了。我现在还很不习惯… 魔术方法?构造函数?…且边学边看

methd

“A method is a function with an implicit first argument, called a receiver.”

这么理解呢,这么说 一个 method 就是 function 的第一个『参数』是一个 receiver 「接收者,接收这个函数的是谁即是方法的所有者」
如果还没明白的话就再通俗一点

receiver = class method = 成员函数

// 下面这个 function 是 ReceiverType (class) 的一个 method, 
// funcName 首字母大写是公有、小写是私有
// ReceiverType 可以是结构体、自定义类型、内置类型「PS这个是真的骚」
func (r ReceiverType) funcName(parameters) {}

指针 作为 receiver

go 知道我们需要调用或传递的是指针,你写值他会自动帮你去用指针。理解这个先来理解下面 c 代码

void setZore(int* a) {
    *a = 0;
    // 在 go 中可以理解为
    a = 0; // go 自动给你变成 *a = 0
}
int x = 10;
// 在 C 中必须传递地址 go 中可以是 setZore(x)
setZore(&x);

如果一个 method 的 receiver 是 *T, 你可以在一个 T 类型的实例变量 V 上面调用这个 method,而不需要 &V 去调用这个 method

如果一个 method 的 receiver 是 T,你可以在一个 T 类型的变量 P 上面调用这个 method,而不需要 P 去调用这个 method

继承跟重载

继承跟重载都跟匿名字段一样的

  • 继承:如果匿名字段实现一个 method 包含这个匿名字段的结构体也能使用这个 method
  • 重写:在包含者上面重新定义这个 method,重写匿名字段的方法
type Human struct {
    name string
    age int
}

type Employee struct {
    Human
    wage int
}

func (h *Human) Say() {
    fmt.Printf("Hi, I am %s, My age is %d", h.name, h.age)
}

// Employee 重写 Say
func (e *Employee) Say() {
    fmt.Printf("Hi, I am %s, %d old year, wage is %d per mothn", e.name, e.age, e.wage)
}