這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
《Go語言實戰》讀書筆記,未完待續,歡迎掃碼關注公眾號flysnow_org
,第一時間看後續筆記。
在Go語言中,函數和方法不太一樣,有明確的概念區分。其他語言中,比如Java,一般來說,函數就是方法,方法就是函數,但是在Go語言中,函數是指不屬於任何結構體、類型的方法,也就是說,函數是沒有接收者的;而方法是有接收者的,我們說的方法要麼是屬於一個結構體的,要麼屬於一個新定義的類型的。
函數
函數和方法,雖然概念不同,但是定義非常相似。函數的定義聲明沒有接收者,所以我們直接在go檔案裡,go包之下定義聲明即可。
12345678 |
func main() {sum := add(1, 2)fmt.Println(sum)}func add(a, b int) int {return a + b} |
例子中,我們定義了add
就是一個函數,它的函數簽名是func add(a, b int) int
,沒有接收者,直接定義在go的一個包之下,可以直接調用,比如例子中的main
函數調用了add
函數。
例子中的這個函數名稱是小寫開頭的add
,所以它的範圍只屬於所聲明的包內使用,不能被其他包使用,如果我們把函數名以大寫字母開頭,該函數的範圍就大了,可以被其他包調用。這也是Go語言中大小寫用處,比如Java中,就有專門的關鍵字來聲明範圍private
、protect
、public
等。
12345678910 |
/* 提供的常用庫,有一些常用的方法,方便使用*/package lib// 一個加法實現// 返回a+b的值func Add(a, b int) int {return a + b} |
如上例子中定義的Add
方法就可以被其他包調用。
方法
方法的聲明和函數類似,他們的區別是:方法在定義的時候,會在func
和方法名之間增加一個參數,這個參數就是接收者,這樣我們定義的這個方法就和接收者綁定在了一起,稱之為這個接收者的方法。
1234567 |
type person struct {name string}func (p person) String() string{return "the person name is "+p.name} |
留意例子中,func
和方法名之間增加的參數(p person)
,這個就是接收者。現在我們說,類型person
有了一個String
方法,現在我們看下如何使用它。
1234 |
func main() {p:=person{name:"張三"}fmt.Println(p.String())} |
調用的方法非常簡單,使用類型的變數進行調用即可,類型變數和方法之前是一個.
操作符,表示要調用這個類型變數的某個方法的意思。
Go語言裡有兩種類型的接收者:值接收者和指標接收者。我們上面的例子中,就是使用實值型別接收者的樣本。
使用實值型別接收者定義的方法,在調用的時候,使用的其實是值接收者的一個副本,所以對該值的任何操作,不會影響原來的類型變數。
1234567891011121314151617 |
func main() {p:=person{name:"張三"}p.modify() //值接收者,修改無效fmt.Println(p.String())}type person struct {name string}func (p person) String() string{return "the person name is "+p.name}func (p person) modify(){p.name = "李四"} |
以上的例子,列印出來的值還是張三
,對其進行的修改無效。如果我們使用一個指標作為接收者,那麼就會其作用了,因為指標接收者傳遞的是一個指向原值指標的副本,指標的副本,指向的還是原來類型的值,所以修改時,同時也會影響原來類型變數的值。
1234567891011121314151617 |
func main() {p:=person{name:"張三"}p.modify() //指標接收者,修改有效fmt.Println(p.String())}type person struct {name string}func (p person) String() string{return "the person name is "+p.name}func (p *person) modify(){p.name = "李四"} |
只需要改動一下,變成指標的接收者,就可以完成了修改。
在調用方法的時候,傳遞的接收者本質上都是副本,只不過一個是這個值副本,一是指向這個值指標的副本。指標具有指向原有值的特性,所以修改了指標指向的值,也就修改了原有的值。我們可以簡單的理解為值接收者使用的是值的副本來調用方法,而指標接收者使用實際的值來調用方法。
在上面的例子中,有沒有發現,我們在調用指標接收者方法的時候,使用的也是一個值的變數,並不是一個指標,如果我們使用下面的也是可以的。
12 |
p:=person{name:"張三"}(&p).modify() //指標接收者,修改有效 |
這樣也是可以的。如果我們沒有這麼強制使用指標進行調用,Go的編譯器自動會幫我們取指標,以滿足接收者的要求。
同樣的,如果是一個值接收者的方法,使用指標也是可以調用的,Go編譯器自動會解引用,以滿足接收者的要求,比如例子中定義的String()
方法,也可以這麼調用:
12 |
p:=person{name:"張三"}fmt.Println((&p).String()) |
總之,方法的調用,既可以使用值,也可以使用指標,我們不必要嚴格的遵守這些,Go語言編譯器會幫我們進行自動轉義的,這大大方便了我們開發人員。
不管是使用值接收者,還是指標接收者,一定要搞清楚類型的本質:對類型進行操作的時候,是要改變當前值,還是要建立一個新值進行返回?這些就可以決定我們是採用值傳遞,還是指標傳遞。
多值返回
Go語言支援函數方法的多值返回,也就說我們定義的函數方法可以返回多個值,比如標準庫裡的很多方法,都是返回兩個值,第一個是函數需要返回的值,第二個是出錯時返回的錯誤資訊,這種的好處,我們的出錯異常資訊再也不用像Java一樣使用一個Exception這麼重的方式表示了,非常簡潔。
12345678 |
func main() {file, err := os.Open("/usr/tmp")if err != nil {log.Fatal(err)return}fmt.Println(file)} |
如果返回的值,我們不想使用,可以使用_
進行忽略。
1 |
file, _ := os.Open("/usr/tmp") |
多個值返回的定義也非常簡單,看個例子。
123 |
func add(a, b int) (int, error) {return a + b, nil} |
函數方法聲明定義的時候,採用逗號分割,因為時多個返回,還要用括弧括起來。返回的值還是使用return
關鍵字,以逗號分割,和返回的聲明的順序一致。
可變參數
函數方法的參數,可以是任意多個,這種我們稱之為可以變參數,比如我們常用的fmt.Println()
這類函數,可以接收一個可變的參數。
123 |
func main() {fmt.Println("1","2","3")} |
可以變參數,可以是任意多個。我們自己也可以定義可以變參數,可變參數的定義,在類型前加上省略符號…即可。
12345678910 |
func main() {print("1","2","3")}func print (a ...interface{}){for _,v:=range a{fmt.Print(v)}fmt.Println()} |
例子中我們自己定義了一個接受可變參數的函數,效果和fmt.Println()
一樣。
可變參數本質上是一個數組,所以我們向使用數組一樣使用它,比如例子中的 for range
迴圈。
函數方法還有其他一些知識點,比如painc
異常處理,遞迴等,這些在《Go語言實戰》書裡也沒有介紹,這些基礎知識,可以參考Go語言的那本聖經。
《Go語言實戰》讀書筆記,未完待續,歡迎掃碼關注公眾號flysnow_org
,第一時間看後續筆記。
掃碼關注