溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

go語(yǔ)言中如何使用結(jié)構(gòu)體組合函數(shù)

發(fā)布時(shí)間:2021-07-20 15:37:16 來(lái)源:億速云 閱讀:361 作者:Leah 欄目:編程語(yǔ)言

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)go語(yǔ)言中如何使用結(jié)構(gòu)體組合函數(shù),文章內(nèi)容豐富且以專(zhuān)業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

Go語(yǔ)言提供的結(jié)構(gòu)體就是把使用各種數(shù)據(jù)類(lèi)型定義的不同變量組合起來(lái)的高級(jí)數(shù)據(jù)類(lèi)型。閑話(huà)不多說(shuō),看例子:

type Rect struct {
	width float64
	length float64}

上面我們定義了一個(gè)矩形結(jié)構(gòu)體,首先是關(guān)鍵是type表示要定義一個(gè)新的數(shù)據(jù)類(lèi)型了,然后是新的數(shù)據(jù)類(lèi)型名稱(chēng)Rect,最后是struct關(guān)鍵字,表示這個(gè)高級(jí)數(shù)據(jù)類(lèi)型是結(jié)構(gòu)體類(lèi)型。在上面的例子中,因?yàn)閣idth和length的數(shù)據(jù)類(lèi)型相同,還可以寫(xiě)成如下格式:

type Rect struct {
	width, length float64}

好了,來(lái)用結(jié)構(gòu)體干點(diǎn)啥吧,計(jì)算一下矩形面積。

package main

import (
	"fmt"
)

type Rect struct {
	width, length float64
}

func main() {
	var rect Rect
	rect.width = 100
	rect.length = 200
	fmt.Println(rect.width * rect.length)
}

從上面的例子看到,其實(shí)結(jié)構(gòu)體類(lèi)型和基礎(chǔ)數(shù)據(jù)類(lèi)型使用方式差不多,唯一的區(qū)別就是結(jié)構(gòu)體類(lèi)型可以通過(guò).來(lái)訪(fǎng)問(wèn)內(nèi)部的成員。包括給內(nèi)部成員賦值和讀取內(nèi)部成員值。

在上面的例子中,我們是用var關(guān)鍵字先定義了一個(gè)Rect變量,然后對(duì)它的成員賦值。我們也可以使用初始化的方式來(lái)給Rect變量的內(nèi)部成員賦值。

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

當(dāng)然如果你知道結(jié)構(gòu)體成員定義的順序,也可以不使用key:value的方式賦值,直接按照結(jié)構(gòu)體成員定義的順序給它們賦值。

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

輸出結(jié)果為

Width: 100 * Length: 200 = Area: 20000

結(jié)構(gòu)體參數(shù)傳遞方式

我們說(shuō)過(guò),Go函數(shù)的參數(shù)傳遞方式是值傳遞,這句話(huà)對(duì)結(jié)構(gòu)體也是適用的。

package main

import (
	"fmt"
)

type Rect struct {
	width, length float64
}

func double_area(rect Rect) float64 {
	rect.width *= 2
	rect.length *= 2
	return 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

也就說(shuō)雖然在double_area函數(shù)里面我們將結(jié)構(gòu)體的寬度和長(zhǎng)度都加倍,但仍然沒(méi)有影響main函數(shù)里面的rect變量的寬度和長(zhǎng)度。

結(jié)構(gòu)體組合函數(shù)

上面我們?cè)趍ain函數(shù)中計(jì)算了矩形的面積,但是我們覺(jué)得矩形的面積如果能夠作為矩形結(jié)構(gòu)體的“內(nèi)部函數(shù)”提供會(huì)更好。這樣我們就可以直接說(shuō)這個(gè)矩形面積是多少,而不用另外去取寬度和長(zhǎng)度去計(jì)算?,F(xiàn)在我們看看結(jié)構(gòu)體“內(nèi)部函數(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())
}

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

確實(shí)如此,我們看到,雖然main函數(shù)中的rect變量可以直接調(diào)用函數(shù)area()來(lái)獲取矩形面積,但是area()函數(shù)確實(shí)沒(méi)有定義在Rect結(jié)構(gòu)體內(nèi)部,這點(diǎn)和C語(yǔ)言的有很大不同。Go使用組合函數(shù)的方式來(lái)為結(jié)構(gòu)體定義結(jié)構(gòu)體方法。我們仔細(xì)看一下上面的area()函數(shù)定義。

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

這里需要注意一點(diǎn)就是定義在結(jié)構(gòu)體上面的函數(shù)(function)一般叫做方法(method)。

結(jié)構(gòu)體和指針

我們?cè)谥羔樢还?jié)講到過(guò),指針的主要作用就是在函數(shù)內(nèi)部改變傳遞進(jìn)來(lái)變量的值。對(duì)于上面的計(jì)算矩形面積的例子,我們可以修改一下代碼如下:

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 = 100
	rect.length = 200
	fmt.Println("Width:", rect.width, "Length:", rect.length,
		"Area:", rect.area())
}

上面的例子中,使用了new函數(shù)來(lái)創(chuàng)建一個(gè)結(jié)構(gòu)體指針rect,也就是說(shuō)rect的類(lèi)型是*Rect,結(jié)構(gòu)體遇到指針的時(shí)候,你不需要使用*去訪(fǎng)問(wèn)結(jié)構(gòu)體的成員,直接使用.引用就可以了。所以上面的例子中我們直接使用rect.width=100 和rect.length=200來(lái)設(shè)置結(jié)構(gòu)體成員值。因?yàn)檫@個(gè)時(shí)候rect是結(jié)構(gòu)體指針,所以我們定義area()函數(shù)的時(shí)候結(jié)構(gòu)體限定類(lèi)型為*Rect。

其實(shí)在計(jì)算面積的這個(gè)例子中,我們不需要改變矩形的寬或者長(zhǎng)度,所以定義area函數(shù)的時(shí)候結(jié)構(gòu)體限定類(lèi)型仍然為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 = 100
	rect.length = 200
	fmt.Println("Width:", rect.width, "Length:", rect.length,
		"Area:", rect.area())
}

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

至于使不使用結(jié)構(gòu)體指針和使不使用指針的出發(fā)點(diǎn)是一樣的,那就是你是否試圖在函數(shù)內(nèi)部改變傳遞進(jìn)來(lái)的參數(shù)的值。再舉個(gè)例子如下:

package main

import (
	"fmt"
)

type Rect struct {
	width, length float64
}

func (rect *Rect) double_area() float64 {
	rect.width *= 2
	rect.length *= 2
	return rect.width * rect.length
}

func main() {
	var rect = new(Rect)
	rect.width = 100
	rect.length = 200
	fmt.Println(*rect)
	fmt.Println("Double Width:", rect.width, "Double Length:", rect.length,
		"Double Area:", rect.double_area())
	fmt.Println(*rect)
}

這個(gè)例子的輸出是:

{100 200}

Double Width: 200 Double Length: 400 Double Area: 80000

{200 400}

結(jié)構(gòu)體內(nèi)嵌類(lèi)型

我們可以在一個(gè)結(jié)構(gòu)體內(nèi)部定義另外一個(gè)結(jié)構(gòu)體類(lèi)型的成員。例如iPhone也是Phone,我們看下例子:

package main

import (
	"fmt"
)

type Phone struct {
	price int
	color string
}

type IPhone struct {
	phone Phone
	model string
}

func main() {
	var p IPhone
	p.phone.price = 5000
	p.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)
}

輸出結(jié)果為:

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

在上面的例子中,我們?cè)诮Y(jié)構(gòu)體IPhone里面定義了一個(gè)Phone變量phone,然后我們可以像正常的訪(fǎng)問(wèn)結(jié)構(gòu)體成員一樣訪(fǎng)問(wèn)phone的成員數(shù)據(jù)。但是我們?cè)瓉?lái)的意思是“iPhone也是(is-a)Phone”,而這里的結(jié)構(gòu)體IPhone里面定義了一個(gè)phone變量,給人的感覺(jué)就是“iPhone有一個(gè)(has-a)Phone”,挺奇怪的。當(dāng)然Go也知道這種方式很奇怪,所以支持如下做法:

package main

import (
	"fmt"
)

type Phone struct {
	price int
	color string
}

type IPhone struct {
	Phone
	model string
}

func main() {
	var p IPhone
	p.price = 5000
	p.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)
}

輸出結(jié)果為

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

在這個(gè)例子中,我們定義IPhone結(jié)構(gòu)體的時(shí)候,不再定義Phone變量,直接把結(jié)構(gòu)體Phone類(lèi)型定義在那里。然后IPhone就可以像訪(fǎng)問(wèn)直接定義在自己結(jié)構(gòu)體里面的成員一樣訪(fǎng)問(wèn)Phone的成員。

上面的例子中,我們演示了結(jié)構(gòu)體的內(nèi)嵌類(lèi)型以及內(nèi)嵌類(lèi)型的成員訪(fǎng)問(wèn),除此之外,假設(shè)結(jié)構(gòu)體A內(nèi)部定義了一個(gè)內(nèi)嵌結(jié)構(gòu)體B,那么A同時(shí)也可以調(diào)用所有定義在B上面的函數(shù)。

package main

import (
	"fmt"
)

type Phone struct {
	price int
	color string
}

func (phone Phone) ringing() {
	fmt.Println("Phone is ringing...")
}

type IPhone struct {
	Phone
	model string
}

func main() {
	var p IPhone
	p.price = 5000
	p.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()
}

輸出結(jié)果為:

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

接口

我們先看一個(gè)例子,關(guān)于Nokia手機(jī)和iPhone手機(jī)都能夠打電話(huà)的例子。

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 NokiaPhone
	nokia.call()

	var iPhone IPhone
	iPhone.call()
}

我們定義了NokiaPhone和IPhone,它們都有各自的方法call(),表示自己都能夠打電話(huà)。但是我們想一想,是手機(jī)都應(yīng)該能夠打電話(huà),所以這個(gè)不算是NokiaPhone或是IPhone的獨(dú)特特點(diǎn)。否則iPhone不可能賣(mài)這么貴了。

再仔細(xì)看一下接口的定義,首先是關(guān)鍵字type,然后是接口名稱(chēng),最后是關(guān)鍵字interface表示這個(gè)類(lèi)型是接口類(lèi)型。在接口類(lèi)型里面,我們定義了一組方法。

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

	phone = new(NokiaPhone)
	phone.call()

	phone = new(IPhone)
	phone.call()

}

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

I am Nokia, I can call you!

I am iPhone, I can call you!

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

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

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

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 = 0
	for _, phone := range phones {
		totalSales += phone.sales()
	}
	fmt.Println(totalSales)

}

輸出結(jié)果:

14200

上面的例子中,我們定義了一個(gè)手機(jī)數(shù)組,然后計(jì)算手機(jī)的總售價(jià)??梢钥吹?,由于NokiaPhone和IPhone都實(shí)現(xiàn)了sales()方法,所以它們都是Phone類(lèi)型,但是計(jì)算售價(jià)的時(shí)候,Go會(huì)知道調(diào)用哪個(gè)對(duì)象實(shí)現(xiàn)的方法。

接口類(lèi)型還可以作為結(jié)構(gòu)體的數(shù)據(jù)成員。

假設(shè)有個(gè)敗家子,iPhone沒(méi)有出的時(shí)候,買(mǎi)了好幾款Nokia,iPhone出來(lái)后,又買(mǎi)了好多部iPhone,老爸要來(lái)看看這小子一共花了多少錢(qián)。

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 []Phone
	name   string
	age    int
}

func (person Person) total_cost() int {
	var sum = 0
	for _, 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())
}

這個(gè)例子純?yōu)檠菔窘涌谧鳛榻Y(jié)構(gòu)體數(shù)據(jù)成員,如有雷同,純屬巧合。這里面我們定義了一個(gè)Person結(jié)構(gòu)體,結(jié)構(gòu)體內(nèi)部定義了一個(gè)手機(jī)類(lèi)型切片。另外我們定義了Person的total_cost()方法用來(lái)計(jì)算手機(jī)花費(fèi)總額。輸出結(jié)果如下:

Jemy

25

14200

上述就是小編為大家分享的go語(yǔ)言中如何使用結(jié)構(gòu)體組合函數(shù)了,如果剛好有類(lèi)似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI