Go組隊學習 7.結構體、方法、介面
結構體
Go 語言中沒有“類”的概念,也不支援像繼承這種面向物件的概念。但是Go 語言的結構體與“類”都是複合結構體,而且Go 語言中結構體的組合方式比面向物件具有更高的擴充套件性和靈活性。
結構體定義
結構體中欄位的型別可以是任何型別,包括函式型別,介面型別,甚至結構體型別本身。
type identifier struct {
field1 type1
field2 type2
...
}
//例
type Student struct {
Name string
Age int
}
在宣告結構體時我們也可以不給欄位指定名字,例如下面這樣
type Person struct {
ID string
int
}
我們可以看到其中有一個int欄位沒有名字,這種我們稱其為匿名欄位。對於一個結構體來說,每一種資料型別只能有一個匿名欄位。
操作結構體
建立結構體的例項,可以使用如下幾種方法建立,仍然以上面的Student結構體為例。
s1 := new(Student) //第一種方式
s2 := Student{"james", 35} //第二種方式
s3 := &Student { //第三種方式
Name: "LeBron",
Age: 36,
}
標籤
在go語言中結構體除了欄位的名稱和型別外還有一個可選的標籤tag,標記的tag只有reflect包可以訪問到,一般用於orm或者json的資料傳遞,下面這段程式碼演示瞭如何為結構體打標籤。
type Student struct {
Name string `json:"name"`
Age int `json:"age"`
}
我們可以使用go自帶的json包將宣告的結構體變數轉變為json字串。
func ToJson(s *Student) (string, error) {
bytes, err := json.Marshal(s)
if err != nil {
return "", nil
}
return string(bytes), nil
}
如果我們沒有給結構體打標籤輸出的json字串如下所示
{"Name":"james","Age":35}
如果我們給結構體打過標籤之後輸出的json字串如下所示
{"name":"james","age":35}
方法
方法定義
方法與函式類似,只不過在方法定義時會在func和方法名之間增加一個引數,如下所示:
func (r Receiver)func_name(){
// body
}
其中r被稱為方法的接收者,例如我們下面這個例子:
type Person struct {
name string
}
func (p Person) GetName() string {
return p.name
}
其中GetName方法的接收者為p是Person結構體型別,也就是說我們為結構體Person綁定了一個GetName方法,我們可以使用如下的方式進行呼叫。
func main() {
p := Person{
name:"james",
}
fmt.Println(p.GetName())
}
介面
介面定義
介面相當於一種規範,它需要做的是誰想要實現我這個介面要做哪些內容,而不是怎麼做。在go語言中介面的定義如下所示:
type Namer interface {
Method1(param_list) return_type
Method2(param_list) return_type
...
}
實現介面
在go語言中不需要顯示的去實現介面,只要一個型別實現了該介面中定義的所有方法就是預設實現了該介面,而且允許多個型別都實現該介面,也允許一個型別實現多個介面。
案例如下:
type Animal interface {
Eat()
}
type Bird struct {
Name string
}
func (b Bird) Eat() {
fmt.Println(b.Name + "吃蟲")
}
type Dog struct {
Name string
}
func (d Dog) Eat() {
fmt.Println(d.Name + "吃肉")
}
func EatWhat(a Animal) {
a.Eat()
}
func main() {
b := Bird{"Bird"}
d := Dog{"Dog"}
EatWhat(b)
EatWhat(d)
}
在EatWaht函式中是傳遞一個Animal介面型別,上面的Bird和Dog結構體都實現了Animal介面,所以都可以傳遞到函式中去來實現多型特性。
型別斷言
有些時候方法傳遞進來的引數可能是一個介面型別,但是我們要繼續判斷是哪個具體的型別才能進行下一步操作,這時就用到了型別斷言,下面我們通過一個例子來進行講解:
func IsDog(a Animal) bool {
if v, ok := a.(Dog); ok {
fmt.Println(v)
return true
}
return false
}
上面的方法對傳遞進來的引數進行判斷,判斷其是否為Dog型別,如果是Dog型別的話就會將其進行轉換為v,ok用來表示是否斷言成功。
但是如果我們對於一個型別有好多種子型別要進行判斷,這樣寫的話顯然是有些複雜,可以使用如下這種方式:
func WhatType(a Animal) {
switch a.(type) {
case Dog:
fmt.Println("Dog")
case Bird:
fmt.Println("Bird")
default:
fmt.Println("error")
}
}
空介面
因為其內部沒有定義任何方法所以空介面可以表示任何一個型別
var any interface{}
any = 1
fmt.Println(any)
any = "hello"
fmt.Println(any)
any = false
fmt.Println(any)