Go語(yǔ)言七篇入門教程三函數(shù)方法及接口
參考書籍:
《go語(yǔ)言程序設(shè)計(jì)》
1. 函數(shù)
每個(gè)函數(shù)聲明都包含一個(gè)名字,一個(gè)形參列表,一個(gè)可選的返回列表以及函數(shù)體:
func name(parameter-list)(result-list){ body }
形參列表:指定另一組變量的參數(shù)名和參數(shù)類型,這些局部變量都由調(diào)用者提供的提供的實(shí)參傳遞而來(lái)的。
返回列表:指定了函數(shù)返回值的類型。當(dāng)函數(shù)返回一個(gè)未命名的返回值或者沒(méi)有返回值的時(shí)候,返回列表的圓括號(hào)可以忽略。
func FanOne(x float64) float64 { return math.Sqrt(x*x) } fmt.Println(FanOne(3)) // 3
這里的x就是形參,3就是傳入函數(shù)的實(shí)參
// 定義一個(gè)求兩數(shù)之和的函數(shù) func add(a,b int) int { return a + b } func main() { sum := add(1,2) fmt.Println(sum) }
2. 方法
在 Go 語(yǔ)言中,結(jié)構(gòu)體就像是類的一種簡(jiǎn)化形式,那么面向?qū)ο蟪绦騿T可能會(huì)問(wèn):類的方法在哪里呢?在 Go 中有一個(gè)概念,它和方法有著同樣的名字,并且大體上意思相同:Go 方法是作用在接收者(receiver)上的一個(gè)函數(shù),接收者是某種類型的變量。因此方法是一種特殊類型的函數(shù)。
接收者類型可以是(幾乎)任何類型,不僅僅是結(jié)構(gòu)體類型:任何類型都可以有方法,甚至可以是函數(shù)類型,可以是 int、bool、string 或數(shù)組的別名類型。但是接收者不能是一個(gè)接口類型,因?yàn)榻涌谑且粋€(gè)抽象定義,但是方法卻是具體實(shí)現(xiàn);如果這樣做會(huì)引發(fā)一個(gè)編譯錯(cuò)誤:invalid receiver type…
。
最后接收者不能是一個(gè)指針類型,但是它可以是任何其他允許類型的指針。
一個(gè)類型加上它的方法等價(jià)于面向?qū)ο笾械囊粋€(gè)類。一個(gè)重要的區(qū)別是:
在 Go 中,類型的代碼和綁定在它上面的方法的代碼可以不放置在一起,它們可以存在在不同的源文件,唯一的要求是:它們必須是同一個(gè)包的。
類型 T(或 *T)上的所有方法的集合叫做類型 T(或 *T)的方法集(method set)。
因?yàn)榉椒ㄊ呛瘮?shù),所以同樣的,不允許方法重載,即對(duì)于一個(gè)類型只能有一個(gè)給定名稱的方法。但是如果基于接收者類型,是有重載的:具有同樣名字的方法可以在 2 個(gè)或多個(gè)不同的接收者類型上存在,比如在同一個(gè)包里這么做是允許的:
func (a *denseMatrix) Add(b Matrix) Matrix func (a *sparseMatrix) Add(b Matrix) Matrix
別名類型沒(méi)有原始類型上已經(jīng)定義過(guò)的方法。
定義方法的一般格式如下:
func (recv receiver_type) methodName(parameter_list) (return_value_list) { ... }
在方法名之前,func 關(guān)鍵字之后的括號(hào)中指定 receiver。
如果 recv
是receiver
的實(shí)例,Method1
是它的方法名,那么方法調(diào)用遵循傳統(tǒng)的object.name
選擇器符號(hào):recv.Method1()
。
如果recv
是一個(gè)指針,Go 會(huì)自動(dòng)解引用。
如果方法不需要使用 recv 的值,可以用 _ 替換它,比如:
func (_ receiver_type) methodName (parameter_list) (return_value_list) { ... }
recv 就像是面向?qū)ο笳Z(yǔ)言中的 this 或 self,但是 Go 中并沒(méi)有這兩個(gè)關(guān)鍵字。隨個(gè)人喜好,你可以使用 this 或 self 作為 receiver 的名字。下面是一個(gè)結(jié)構(gòu)體上的簡(jiǎn)單方法的例子:
package main import "fmt" type TwoInts struct { a int b int } func main() { two1 := new(TwoInts) two1.a = 12 two1.b = 10 fmt.Printf("The sum is: %d\n", two1.AddThem()) fmt.Printf("Add them to the param: %d\n", two1.AddToParam(20)) two2 := TwoInts{3, 4} fmt.Printf("The sum is: %d\n", two2.AddThem()) } func (tn *TwoInts) AddThem() int { return tn.a + tn.b } func (tn *TwoInts) AddToParam(param int) int { return tn.a + tn.b + param }
輸出:
The sum is: 22
Add them to the param: 42
The sum is: 7
方法是可以重載的,這里的話就有那么一點(diǎn)面向?qū)ο髢?nèi)味了~
比如
func (a *aaa) Fan(){ } func (a *bbb)Fan(){ }
這種是可以的~
3. 接口
Go 語(yǔ)言不是一種 “傳統(tǒng)” 的面向?qū)ο缶幊陶Z(yǔ)言:它里面沒(méi)有類和繼承的概念。
接口是golang中實(shí)現(xiàn)多態(tài)性的好途徑。接口類型是對(duì)其他類型行為的概括與抽象,對(duì)于一個(gè)具體的類型,無(wú)需聲明它實(shí)現(xiàn)了哪些接口,只提供接口所必須的方法即可。
之前介紹的類型都是具體類型。go語(yǔ)言中還有一種類型稱為接口類型。接口是一種抽象類型,他并沒(méi)有暴露所含數(shù)據(jù)的布局或者內(nèi)部結(jié)構(gòu),當(dāng)然也沒(méi)有那些數(shù)據(jù)的基本操作,它所提供的僅僅是一些方法而已,如果你拿到了一個(gè)接口,你無(wú)從知道他是什么,但是你能知道的僅僅是它能做什么,或者更精確地講,僅僅是它提供了哪些方法。
接口定義了一組方法(方法集),但是這些方法不包含(實(shí)現(xiàn))代碼:它們沒(méi)有被實(shí)現(xiàn)(它們是抽象的)。接口里也不能包含變量。
通過(guò)如下格式定義接口:
type Namer interface { Method1(param_list) return_type Method2(param_list) return_type ... }
上面的 Namer
是一個(gè) 接口類型。
(按照約定,只包含一個(gè)方法的)接口的名字由方法名加 er
后綴組成,例如 Printer
、Reader
、Writer
、Logger
、Converter
等等。還有一些不常用的方式(當(dāng)后綴 er
不合適時(shí)),比如 Recoverable
,此時(shí)接口名以 able
結(jié)尾,或者以 I
開頭(像 .NET
或 Java
中那樣)。
Go 語(yǔ)言中的接口都很簡(jiǎn)短,通常它們會(huì)包含 0 個(gè)、最多 3 個(gè)方法。
不像大多數(shù)面向?qū)ο缶幊陶Z(yǔ)言,在 Go 語(yǔ)言中接口可以有值,一個(gè)接口類型的變量或一個(gè) 接口值 :var ai Namer
,ai
是一個(gè)多字(multiword)數(shù)據(jù)結(jié)構(gòu),它的值是 nil
。它本質(zhì)上是一個(gè)指針,雖然不完全是一回事。指向接口值的指針是非法的,它們不僅一點(diǎn)用也沒(méi)有,還會(huì)導(dǎo)致代碼錯(cuò)誤。
類型(比如結(jié)構(gòu)體)可以實(shí)現(xiàn)某個(gè)接口的方法集;這個(gè)實(shí)現(xiàn)可以描述為,該類型的變量上的每一個(gè)具體方法所組成的集合,包含了該接口的方法集。實(shí)現(xiàn)了 Namer
接口的類型的變量可以賦值給 ai
(即 receiver
的值),方法表指針(method table ptr)就指向了當(dāng)前的方法實(shí)現(xiàn)。當(dāng)另一個(gè)實(shí)現(xiàn)了 Namer
接口的類型的變量被賦給 ai
,receiver
的值和方法表指針也會(huì)相應(yīng)改變。
類型不需要顯式聲明它實(shí)現(xiàn)了某個(gè)接口:接口被隱式地實(shí)現(xiàn)。多個(gè)類型可以實(shí)現(xiàn)同一個(gè)接口。
實(shí)現(xiàn)某個(gè)接口的類型(除了實(shí)現(xiàn)接口方法外)可以有其他的方法。一個(gè)類型可以實(shí)現(xiàn)多個(gè)接口。
接口類型可以包含一個(gè)實(shí)例的引用, 該實(shí)例的類型實(shí)現(xiàn)了此接口(接口是動(dòng)態(tài)類型)。
即使接口在類型之后才定義,二者處于不同的包中,被單獨(dú)編譯:只要類型實(shí)現(xiàn)了接口中的方法,它就實(shí)現(xiàn)了此接口。
所有這些特性使得接口具有很大的靈活性。
第一個(gè)例子:
package main import "fmt" type Shaper interface { Area() float32 } type Square struct { side float32 } func (sq *Square) Area() float32 { return sq.side * sq.side } func main() { sq1 := new(Square) sq1.side = 5 var areaIntf Shaper areaIntf = sq1 // shorter,without separate declaration: // areaIntf := Shaper(sq1) // or even: // areaIntf := sq1 fmt.Printf("The square has area: %f\n", areaIntf.Area()) }
輸出:
The square has area: 25.000000
上面的程序定義了一個(gè)結(jié)構(gòu)體 Square
和一個(gè)接口 Shaper
,接口有一個(gè)方法 Area()
。
在 main()
方法中創(chuàng)建了一個(gè) Square
的實(shí)例。在主程序外邊定義了一個(gè)接收者類型是 Square
方法的 Area()
,用來(lái)計(jì)算正方形的面積:結(jié)構(gòu)體 Square
實(shí)現(xiàn)了接口 Shaper
。
所以可以將一個(gè) Square
類型的變量賦值給一個(gè)接口類型的變量:areaIntf = sq1
。
現(xiàn)在接口變量包含一個(gè)指向 Square
變量的引用,通過(guò)它可以調(diào)用 Square
上的方法 Area()
。當(dāng)然也可以直接在 Square
的實(shí)例上調(diào)用此方法,但是在接口實(shí)例上調(diào)用此方法更令人興奮,它使此方法更具有一般性。接口變量里包含了接收者實(shí)例的值和指向?qū)?yīng)方法表的指針。
這是 多態(tài) 的 Go 版本,多態(tài)是面向?qū)ο缶幊讨幸粋€(gè)廣為人知的概念:根據(jù)當(dāng)前的類型選擇正確的方法,或者說(shuō):同一種類型在不同的實(shí)例上似乎表現(xiàn)出不同的行為。
如果 Square
沒(méi)有實(shí)現(xiàn) Area()
方法,編譯器將會(huì)給出清晰的錯(cuò)誤信息:
cannot use sq1 (type *Square) as type Shaper in assignment: *Square does not implement Shaper (missing Area method)
如果 Shaper
有另外一個(gè)方法 Perimeter()
,但是Square
沒(méi)有實(shí)現(xiàn)它,即使沒(méi)有人在 Square
實(shí)例上調(diào)用這個(gè)方法,編譯器也會(huì)給出上面同樣的錯(cuò)誤。
擴(kuò)展一下上面的例子,類型 Rectangle
也實(shí)現(xiàn)了 Shaper
接口。接著創(chuàng)建一個(gè) Shaper
類型的數(shù)組,迭代它的每一個(gè)元素并在上面調(diào)用 Area()
方法,以此來(lái)展示多態(tài)行為:
package main import "fmt" type Shaper interface { Area() float32 } type Square struct { side float32 } func (sq *Square) Area() float32 { return sq.side * sq.side } type Rectangle struct { length, width float32 } func (r Rectangle) Area() float32 { return r.length * r.width } func main() { r := Rectangle{5, 3} // Area() of Rectangle needs a value q := &Square{5} // Area() of Square needs a pointer // shapes := []Shaper{Shaper(r), Shaper(q)} // or shorter shapes := []Shaper{r, q} fmt.Println("Looping through shapes for area ...") for n, _ := range shapes { fmt.Println("Shape details: ", shapes[n]) fmt.Println("Area of this shape is: ", shapes[n].Area()) } }
輸出:
Looping through shapes for area ... Shape details: {5 3} Area of this shape is: 15 Shape details: &{5} Area of this shape is: 25
在調(diào)用 shapes[n].Area()
這個(gè)時(shí),只知道 shapes[n]
是一個(gè) Shaper
對(duì)象,最后它搖身一變成為了一個(gè) Square
或 Rectangle
對(duì)象,并且表現(xiàn)出了相對(duì)應(yīng)的行為。
也許從現(xiàn)在開始你將看到通過(guò)接口如何產(chǎn)生 更干凈、更簡(jiǎn)單 及 更具有擴(kuò)展性 的代碼。在 11.12.3 中將看到在開發(fā)中為類型添加新的接口是多么的容易。
下面是一個(gè)更具體的例子:有兩個(gè)類型 stockPosition
和 car
,它們都有一個(gè) getValue()
方法,我們可以定義一個(gè)具有此方法的接口 valuable
。接著定義一個(gè)使用 valuable
類型作為參數(shù)的函數(shù) showValue()
,所有實(shí)現(xiàn)了 valuable
接口的類型都可以用這個(gè)函數(shù)。
package main import "fmt" type stockPosition struct { ticker string sharePrice float32 count float32 } /* method to determine the value of a stock position */ func (s stockPosition) getValue() float32 { return s.sharePrice * s.count } type car struct { make string model string price float32 } //使用方法去獲取車的值 func (c car) getValue() float32 { return c.price } /* contract that defines different things that have value */ type valuable interface { getValue() float32 } func showValue(asset valuable) { fmt.Printf("Value of the asset is %f\n", asset.getValue()) } func main() { var o valuable = stockPosition{"GOOG", 577.20, 4} showValue(o) o = car{"BMW", "M3", 66500} showValue(o) }
輸出:
Value of the asset is 2308.800049 Value of the asset is 66500.000000
以上就是Go語(yǔ)言七篇入門教程三函數(shù)方法及接口的詳細(xì)內(nèi)容,更多關(guān)于Go語(yǔ)言函數(shù)方法及接口的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
如何學(xué)習(xí)Go
如果你是小白,你可以這樣學(xué)習(xí)Go語(yǔ)言~
七篇入門Go語(yǔ)言
第二篇:程序結(jié)構(gòu)&&數(shù)據(jù)類型的介紹
第五篇:文件及包的操作與處理
第六篇:網(wǎng)絡(luò)編程
第七篇:GC垃圾回收三色標(biāo)記
相關(guān)文章
go如何使用gin結(jié)合jwt做登錄功能簡(jiǎn)單示例
jwt全稱Json web token,是一種認(rèn)證和信息交流的工具,這篇文章主要給大家介紹了關(guān)于go如何使用gin結(jié)合jwt做登錄功能的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01golang?gorm錯(cuò)誤處理事務(wù)以及日志用法示例
這篇文章主要為大家介紹了golang?gorm錯(cuò)誤處理事務(wù)以及日志用法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04在Golang中實(shí)現(xiàn)RSA算法的加解密操作詳解
RSA 是一種非對(duì)稱加密算法,廣泛使用于數(shù)據(jù)的安全傳輸,crypto/rsa 是 Golang 中實(shí)現(xiàn)了 RSA 算法的一個(gè)標(biāo)準(zhǔn)庫(kù),提供了生成公私鑰對(duì)、加解密數(shù)據(jù)、簽名和驗(yàn)簽等功能,本文給大家介紹了在Golang中實(shí)現(xiàn)RSA算法的加解密操作,需要的朋友可以參考下2023-12-12深入理解Go語(yǔ)言實(shí)現(xiàn)多態(tài)?
本文主要介紹了Go語(yǔ)言實(shí)現(xiàn)多態(tài),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05Go語(yǔ)言特點(diǎn)及基本數(shù)據(jù)類型使用詳解
這篇文章主要為大家介紹了Go語(yǔ)言特點(diǎn)及基本數(shù)據(jù)類型使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03詳解go語(yǔ)言判斷管道是否關(guān)閉的常見(jiàn)誤區(qū)
這篇文章主要想和大家一起探討一下在Go語(yǔ)言中,我們是否可以使用讀取管道時(shí)的第二個(gè)返回值來(lái)判斷管道是否關(guān)閉,文中的示例代碼講解詳細(xì),有興趣的可以了解下2023-10-10利用Go語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單Ping過(guò)程的方法
相信利用各種語(yǔ)言實(shí)現(xiàn)Ping已經(jīng)是大家喜聞樂(lè)見(jiàn)的事情了,網(wǎng)絡(luò)上利用Golang實(shí)現(xiàn)Ping已經(jīng)有比較詳細(xì)的代碼示例,但大多是僅僅是實(shí)現(xiàn)了Request過(guò)程,而對(duì)Response的回顯內(nèi)容并沒(méi)有做接收。而Ping程序不僅僅是發(fā)送一個(gè)ICMP,更重要的是如何接收并進(jìn)行統(tǒng)計(jì)。2016-09-09