3. Struct 與 物件導向 |《 刻意學習 Golang 》

jerrkill發表於2019-03-08

go 的變數是 name type, 老是寫成 c 的 type name
go 的 type name struct c 的typedef struct name

對比 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)
}

高度自律,深度思考,以勤補拙

相關文章