日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

换个语言学一下 Golang (9)——结构体和接口

發(fā)布時間:2024/4/15 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 换个语言学一下 Golang (9)——结构体和接口 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
換個語言學一下 Golang (9)——結構體和接口

基本上到這里的時候,就是上了一個臺階了。Go的精華特點即將展開。

結構體定義

上面我們說過Go的指針和C的不同,結構體也是一樣的。Go是一門刪繁就簡的語言,一切令人困惑的特性都必須去掉。

簡單來講,Go提供的結構體就是把使用各種數(shù)據(jù)類型定義的不同變量組合起來的高級數(shù)據(jù)類型。閑話不多說,看例子:

type Rect struct {width float64length float64 }

上面我們定義了一個矩形結構體,首先是關鍵是type表示要定義一個新的數(shù)據(jù)類型了,然后是新的數(shù)據(jù)類型名稱Rect,最后是struct關鍵字,表示這個高級數(shù)據(jù)類型是結構體類型。在上面的例子中,因為width和length的數(shù)據(jù)類型相同,還可以寫成如下格式:

type Rect struct {width, length float64 }

好了,來用結構體干點啥吧,計算一下矩形面積。

package main import ("fmt" ) type Rect struct {width, length float64 } func main() {var rect Rectrect.width = 100rect.length = 200fmt.Println(rect.width * rect.length) }

從上面的例子看到,其實結構體類型和基礎數(shù)據(jù)類型使用方式差不多,唯一的區(qū)別就是結構體類型可以通過.來訪問內部的成員。包括給內部成員賦值和讀取內部成員值。

在上面的例子中,我們是用var關鍵字先定義了一個Rect變量,然后對它的成員賦值。我們也可以使用初始化的方式來給Rect變量的內部成員賦值。

package main import ("fmt" ) type Rect struct {width, length float64 } func main() {var rect = Rect{width: 100, length: 200}fmt.Println(rect.width * rect.length) }

當然如果你知道結構體成員定義的順序,也可以不使用key:value的方式賦值,直接按照結構體成員定義的順序給它們賦值。

package main import ("fmt" ) type Rect struct {width, length float64 } func main() {var rect = Rect{100, 200}fmt.Println("Width:", rect.width, "* Length:",rect.length, "= Area:", rect.width*rect.length) }

結構體參數(shù)傳遞方式

我們說過,Go函數(shù)的參數(shù)傳遞方式是值傳遞,這句話對結構體也是適用的。?

package main import ("fmt" ) type Rect struct {width, length float64 } func double_area(rect Rect) float64 {rect.width *= 2rect.length *= 2return rect.width * rect.length } func main() {var rect = Rect{100, 200}fmt.Println(double_area(rect))fmt.Println("Width:", rect.width, "Length:", rect.length) }

輸出結果為

80000 Width: 100 Length: 200

也就說雖然在double_area函數(shù)里面我們將結構體的寬度和長度都加倍,但仍然沒有影響main函數(shù)里面的rect變量的寬度和長度。

?

結構體組合函數(shù)

?

上面我們在main函數(shù)中計算了矩形的面積,但是我們覺得矩形的面積如果能夠作為矩形結構體的“內部函數(shù)”提供會更好。這樣我們就可以直接說這個矩形面積是多少,而不用另外去取寬度和長度去計算。現(xiàn)在我們看看結構體“內部函數(shù)”定義方法:

package main import ("fmt" ) type Rect struct {width, length float64 } func (rect Rect) area() float64 {return rect.width * rect.length } func main() {var rect = Rect{100, 200}fmt.Println("Width:", rect.width, "Length:", rect.length,"Area:", rect.area()) }

咦?這個是什么“內部方法”,根本沒有定義在Rect數(shù)據(jù)類型的內部啊?

確實如此,我們看到,雖然main函數(shù)中的rect變量可以直接調用函數(shù)area()來獲取矩形面積,但是area()函數(shù)確實沒有定義在Rect結構體內部,這點和C語言的有很大不同。Go使用組合函數(shù)的方式來為結構體定義結構體方法。我們仔細看一下上面的area()函數(shù)定義。

首先是關鍵字func表示這是一個函數(shù),第二個參數(shù)是結構體類型和實例變量,第三個是函數(shù)名稱,第四個是函數(shù)返回值。這里我們可以看出area()函數(shù)和普通函數(shù)定義的區(qū)別就在于area()函數(shù)多了一個結構體類型限定。這樣一來Go就知道了這是一個為結構體定義的方法。

這里需要注意一點就是定義在結構體上面的函數(shù)(function)一般叫做方法(method)

?

結構體和指針

我們在指針一節(jié)講到過,指針的主要作用就是在函數(shù)內部改變傳遞進來變量的值。對于上面的計算矩形面積的例子,我們可以修改一下代碼如下:

package main import ("fmt" ) type Rect struct {width, length float64 } func (rect *Rect) area() float64 {return rect.width * rect.length } func main() {var rect = new(Rect)rect.width = 100rect.length = 200fmt.Println("Width:", rect.width, "Length:", rect.length,"Area:", rect.area()) }

上面的例子中,使用了new函數(shù)來創(chuàng)建一個結構體指針rect,也就是說rect的類型是*Rect,結構體遇到指針的時候,你不需要使用*去訪問結構體的成員,直接使用.引用就可以了。所以上面的例子中我們直接使用rect.width=100?和rect.length=200來設置結構體成員值。因為這個時候rect是結構體指針,所以我們定義area()函數(shù)的時候結構體限定類型為*Rect。

其實在計算面積的這個例子中,我們不需要改變矩形的寬或者長度,所以定義area函數(shù)的時候結構體限定類型仍然為Rect也是可以的。如下:

package main import ("fmt" ) type Rect struct {width, length float64 } func (rect Rect) area() float64 {return rect.width * rect.length } func main() {var rect = new(Rect)rect.width = 100rect.length = 200fmt.Println("Width:", rect.width, "Length:", rect.length,"Area:", rect.area()) }

這里Go足夠聰明,所以rect.area()也是可以的。

至于使不使用結構體指針和使不使用指針的出發(fā)點是一樣的,那就是你是否試圖在函數(shù)內部改變傳遞進來的參數(shù)的值。再舉個例子如下:

package main import ("fmt" ) type Rect struct {width, length float64 } func (rect *Rect) double_area() float64 {rect.width *= 2rect.length *= 2return rect.width * rect.length } func main() {var rect = new(Rect)rect.width = 100rect.length = 200fmt.Println(*rect)fmt.Println("Double Width:", rect.width, "Double Length:", rect.length,"Double Area:", rect.double_area())fmt.Println(*rect) }

這個例子的輸出是:

{100 200} Double Width: 200 Double Length: 400 Double Area: 80000 {200 400}

結構體內嵌類型

我們可以在一個結構體內部定義另外一個結構體類型的成員。例如iPhone也是Phone,我們看下例子: package main import ("fmt" ) type Phone struct {price intcolor string } type IPhone struct {phone Phonemodel string } func main() {var p IPhonep.phone.price = 5000p.phone.color = "Black"p.model = "iPhone 5"fmt.Println("I have a iPhone:")fmt.Println("Price:", p.phone.price)fmt.Println("Color:", p.phone.color)fmt.Println("Model:", p.model) }

輸出結果為

I have a iPhone: Price: 5000 Color: Black Model: iPhone 5

在上面的例子中,我們在結構體IPhone里面定義了一個Phone變量phone,然后我們可以像正常的訪問結構體成員一樣訪問phone的成員數(shù)據(jù)。但是我們原來的意思是“iPhone也是(is-a)Phone”,而這里的結構體IPhone里面定義了一個phone變量,給人的感覺就是“iPhone有一個(has-a)Phone”,挺奇怪的。當然Go也知道這種方式很奇怪,所以支持如下做法:

package main import ("fmt" ) type Phone struct {price intcolor string } type IPhone struct {Phonemodel string } func main() {var p IPhonep.price = 5000p.color = "Black"p.model = "iPhone 5"fmt.Println("I have a iPhone:")fmt.Println("Price:", p.price)fmt.Println("Color:", p.color)fmt.Println("Model:", p.model) }

輸出結果為

I have a iPhone: Price: 5000 Color: Black Model: iPhone 5

在這個例子中,我們定義IPhone結構體的時候,不再定義Phone變量,直接把結構體Phone類型定義在那里。然后IPhone就可以像訪問直接定義在自己結構體里面的成員一樣訪問Phone的成員,有點類似與繼承。

上面的例子中,我們演示了結構體的內嵌類型以及內嵌類型的成員訪問,除此之外,假設結構體A內部定義了一個內嵌結構體B,那么A同時也可以調用所有定義在B上面的函數(shù)。

package main import ("fmt" ) type Phone struct {price intcolor string } func (phone Phone) ringing() {fmt.Println("Phone is ringing...") } type IPhone struct {Phonemodel string } func main() {var p IPhonep.price = 5000p.color = "Black"p.model = "iPhone 5"fmt.Println("I have a iPhone:")fmt.Println("Price:", p.price)fmt.Println("Color:", p.color)fmt.Println("Model:", p.model)p.ringing() }

輸出結果為:

I have a iPhone: Price: 5000 Color: Black Model: iPhone 5 Phone is ringing...

接口

我們先看一個例子,關于Nokia手機和iPhone手機都能夠打電話的例子。

package main import ("fmt" ) type NokiaPhone struct { } func (nokiaPhone NokiaPhone) call() {fmt.Println("I am Nokia, I can call you!") } type IPhone struct { } func (iPhone IPhone) call() {fmt.Println("I am iPhone, I can call you!") } func main() {var nokia NokiaPhonenokia.call()var iPhone IPhoneiPhone.call() }

我們定義了NokiaPhone和IPhone,它們都有各自的方法call(),表示自己都能夠打電話。但是我們想一想,是手機都應該能夠打電話,所以這個不算是NokiaPhone或是IPhone的獨特特點。否則iPhone不可能賣這么貴了。

再仔細看一下接口的定義,首先是關鍵字type,然后是接口名稱,最后是關鍵字interface表示這個類型是接口類型。在接口類型里面,我們定義了一組方法。

Go語言提供了一種接口功能,它把所有的具有共性的方法定義在一起,任何其他類型只要實現(xiàn)了這些方法就是實現(xiàn)了這個接口,不一定非要顯式地聲明要去實現(xiàn)哪些接口啦。比如上面的手機的call()方法,就完全可以定義在接口Phone里面,而NokiaPhone和IPhone只要實現(xiàn)了這個接口就是一個Phone。

package main import ("fmt" ) type Phone interface {call() } type NokiaPhone struct { } func (nokiaPhone NokiaPhone) call() {fmt.Println("I am Nokia, I can call you!") } type IPhone struct { } func (iPhone IPhone) call() {fmt.Println("I am iPhone, I can call you!") } func main() {var phone Phonephone = new(NokiaPhone)phone.call()phone = new(IPhone)phone.call() }

在上面的例子中,我們定義了一個接口Phone,接口里面有一個方法call(),僅此而已。然后我們在main函數(shù)里面定義了一個Phone類型變量,并分別為之賦值為NokiaPhone和IPhone。然后調用call()方法,輸出結果如下:

I am Nokia, I can call you! I am iPhone, I can call you!

以前我們說過,Go語言式靜態(tài)類型語言,變量的類型在運行過程中不能改變。但是在上面的例子中,phone變量好像先定義為Phone類型,然后是NokiaPhone類型,最后成為了IPhone類型,真的是這樣嗎?

原來,在Go語言里面,一個類型A只要實現(xiàn)了接口X所定義的全部方法,那么A類型的變量也是X類型的變量。在上面的例子中,NokiaPhone和IPhone都實現(xiàn)了Phone接口的call()方法,所以它們都是Phone,這樣一來是不是感覺正常了一些。

我們?yōu)镻hone添加一個方法sales(),再來熟悉一下接口用法。

package main import ("fmt" ) type Phone interface {call()sales() int } type NokiaPhone struct {price int } func (nokiaPhone NokiaPhone) call() {fmt.Println("I am Nokia, I can call you!") } func (nokiaPhone NokiaPhone) sales() int {return nokiaPhone.price } type IPhone struct {price int } func (iPhone IPhone) call() {fmt.Println("I am iPhone, I can call you!") } func (iPhone IPhone) sales() int {return iPhone.price } func main() {var phones = [5]Phone{NokiaPhone{price: 350},IPhone{price: 5000},IPhone{price: 3400},NokiaPhone{price: 450},IPhone{price: 5000},}var totalSales = 0for _, phone := range phones {totalSales += phone.sales()}fmt.Println(totalSales) }

輸出結果:

14200

上面的例子中,我們定義了一個手機數(shù)組,然后計算手機的總售價。可以看到,由于NokiaPhone和IPhone都實現(xiàn)了sales()方法,所以它們都是Phone類型,但是計算售價的時候,Go會知道調用哪個對象實現(xiàn)的方法。

接口類型還可以作為結構體的數(shù)據(jù)成員。

假設有個敗家子,iPhone沒有出的時候,買了好幾款Nokia,iPhone出來后,又買了好多部iPhone,老爸要來看看這小子一共花了多少錢。

package main import ("fmt" ) type Phone interface {sales() int } type NokiaPhone struct {price int } func (nokiaPhone NokiaPhone) sales() int {return nokiaPhone.price } type IPhone struct {price int } func (iPhone IPhone) sales() int {return iPhone.price } type Person struct {phones []Phonename stringage int } func (person Person) total_cost() int {var sum = 0for _, phone := range person.phones {sum += phone.sales()}return sum } func main() {var bought_phones = [5]Phone{NokiaPhone{price: 350},IPhone{price: 5000},IPhone{price: 3400},NokiaPhone{price: 450},IPhone{price: 5000},}var person = Person{name: "Jemy", age: 25, phones: bought_phones[:]}fmt.Println(person.name)fmt.Println(person.age)fmt.Println(person.total_cost()) }

這個例子純?yōu)檠菔窘涌谧鳛榻Y構體數(shù)據(jù)成員。這里面我們定義了一個Person結構體,結構體內部定義了一個手機類型切片。另外我們定義了Person的total_cost()方法用來計算手機花費總額。輸出結果如下:

Jemy 25 14200

小結

Go的結構體和接口的實現(xiàn)方法可謂刪繁就簡,去除了很多別的語言令人困惑的地方,而且學習難度也不大,很容易上手。不過由于思想比較獨到,也有可能會有人覺得功能太簡單而無用,這個就各有看法了,不過在逐漸的使用過程中,我們會慢慢領悟到這種設計所帶來的好處,以及所避免的問題。

?

?

?

?

?

posted on 2019-06-27 13:20 tianyamoon 閱讀(...) 評論(...) 編輯 收藏

轉載于:https://www.cnblogs.com/tianyamoon/p/11096412.html

總結

以上是生活随笔為你收集整理的换个语言学一下 Golang (9)——结构体和接口的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。