中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

免費(fèi)com域名注冊網(wǎng)站上海seo推廣整站

免費(fèi)com域名注冊網(wǎng)站,上海seo推廣整站,專業(yè)網(wǎng)站建設(shè)課程,怎建立自己網(wǎng)站做淘寶客文章目錄 方法方法聲明基于指針對象的方法nil 也是合法的接收器類型 通過嵌入結(jié)構(gòu)體來擴(kuò)展類型方法值和方法表達(dá)式封裝 方法 今天我們來復(fù)習(xí) Golang 語法中的“方法”,方法屬于某個(gè)「類型」,它屬于 Go 的語法糖,本質(zhì)上是一個(gè)函數(shù)&#xff0c…

文章目錄

  • 方法
    • 方法聲明
    • 基于指針對象的方法
      • nil 也是合法的接收器類型
    • 通過嵌入結(jié)構(gòu)體來擴(kuò)展類型
    • 方法值和方法表達(dá)式
    • 封裝

方法

今天我們來復(fù)習(xí) Golang 語法中的“方法”,方法屬于某個(gè)「類型」,它屬于 Go 的語法糖,本質(zhì)上是一個(gè)函數(shù),使得這個(gè)類型可以像在調(diào)用它的“類方法”一樣來調(diào)用這個(gè)函數(shù)。
在這里插入圖片描述

方法聲明

在聲明函數(shù)時(shí),在函數(shù)名前面放上一個(gè)變量,這個(gè)函數(shù)就成為了變量所對應(yīng)類型的方法,這個(gè)方法將成為該類型的獨(dú)占方法。一個(gè)方法聲明的例子如下:

type Point struct{ X, Y float64 }func Distance(p, q Point) float64 {return math.Hypot(q.X-p.X, q.Y-p.Y)
}func (p Point) Distance(q Point) float64 {return math.Hypot(q.X-p.X, q.Y-p.Y)
}

上述方法定義時(shí),Point類型的p是方法的接收器(receiver),在 C++ 當(dāng)中我們使用this作為接收器,而在 Python 中我們使用self作為接收器。在 Golang 當(dāng)中,我們可以自定義接收器的名稱,《Go 語言圣經(jīng)》當(dāng)中給出的建議是使用類型的第一個(gè)字母。

調(diào)用方法的例子如下:

p := Point{1, 2}
q := Point{4, 6}
fmt.Println(Distance(p, q)) // "5", function call
fmt.Println(p.Distance(q))  // "5", method call

可以看到,函數(shù)DistancePoint類型的Distance方法不會產(chǎn)生沖突。

在 Go 當(dāng)中,美中類型都有其方法的命名空間,我們在使用 Distance 這個(gè)名字的時(shí)候,不同的 Distance 調(diào)用指向了不同類型里的 Distance 方法。下例是一個(gè)更復(fù)雜的例子,它定義了一個(gè)Path類型,它本質(zhì)是[]Point,我們進(jìn)一步為它也定義一個(gè)Distance方法:

// A Path is a journey connecting the points with straight lines.
type Path []Point
// Distance returns the distance traveled along the path.
func (path Path) Distance() float64 {sum := 0.0for i := range path {if i > 0 {sum += path[i-1].Distance(path[i])}}return sum
}

兩個(gè)Distance有不同的類型,但兩個(gè)方法之間沒有任何關(guān)系。調(diào)用新方法來計(jì)算三角形周長:

perim := Path{{1, 1},{5, 1},{5, 4},{1, 1},
}
fmt.Println(perim.Distance()) // "12"

基于指針對象的方法

在最開始我們提到,「方法」是 Golang 的語法糖,其底層本質(zhì)上仍然是一個(gè)函數(shù),方法的接收器將作為這個(gè)函數(shù)的第一個(gè)參數(shù)。因此,如果我們想要在方法當(dāng)中修改接收器的值,或是這個(gè)方法的接收器占用的內(nèi)存非常大,我們不希望在調(diào)用方法時(shí)進(jìn)行值拷貝,此時(shí)就可以使用指針對象作為接收器:

func (p *Point) ScaleBy(factor float64) {p.X *= factorp.Y *= factor
}

方法的名字是(*Point).ScaleBy,此處的括號是必須的,否則會被理解為*(Point.ScaleBy)(函數(shù)值的指針)。

在真實(shí)開發(fā)環(huán)境中,一般約定如果Point這個(gè)類型有「以指針作為接收器」的方法,那么Point的所有方法必須有一個(gè)指針接收器,即使某些方法不需要指針接收器。此外,為了避免歧義,如果一個(gè)類型本身是一個(gè)指針的話(比如type P *int),那么它不允許出現(xiàn)在接收器中。

想要調(diào)用指針類型方法(*Point).ScaleBy,只需要提供一個(gè)Point類型的指針并調(diào)用該方法即可。比如:

r := &Point{1, 2}
r.ScaleBy(2)
fmt.Println(*r) // "{2, 4}"

以上的寫法較為笨拙,因?yàn)樾枰覀冿@式地將值轉(zhuǎn)為指針再調(diào)用指針接收器方法。 Go 存在一種語法糖,也就是如果一個(gè)值類型直接調(diào)用它的以指針作為接收器的方法,那么 Go 的編譯器會隱式地幫我們用這個(gè)值的指針來調(diào)用指針方法。比如:

p := Point{1, 2}
p.ScaleBy(3)

上述代碼中,編譯器隱式地幫我們用&p調(diào)用ScaleBy。這種簡寫方法僅適用于變量,因?yàn)樽兞康牡刂肥谴_定的,如果Point是某個(gè) struct 的成員,或者是 slice 當(dāng)中的元素,由于我們無法取到 struct 成員的地址,且 slice 底層的數(shù)組可能會修改從而導(dǎo)致地址改變,因此對于這類值,我們不能調(diào)用它們的以指針作為接收器的方法。臨時(shí)變量的內(nèi)存地址也無法取到,因此也不能直接對臨時(shí)變量調(diào)用指針接收器方法:

Point{1, 2}.ScaleBy(2)	// ? 不能對臨時(shí)變量調(diào)用指針接收器方法

此外,對于一個(gè)指針類型,如果它具有以值作為接收器的方法,那么這個(gè)指針也可以直接調(diào)用值接收器方法,Go 編譯器會隱式地幫我們解指針引用。

總結(jié)一下,在每一個(gè)合法的方法調(diào)用表達(dá)式中,存在以下三種情況,都是可以正常運(yùn)行的:

第一種情況是方法調(diào)用者的類型與其方法接收器的類型匹配,即二者都是值T或指針*T

Point{1, 2}.Distance(q)		// Distance 是以值為接收器的方法
pptr.ScaleBy(2)				// pptr 是 Point 的指針, ScaleBy 是以指針為接收器的方法

第二種是:如果接收器的實(shí)參,即方法的調(diào)用者類型是值T,但接收器的形參類型是*T,這種情況下編譯器會隱式地幫助我們?nèi)?shí)參的地址:

p.ScaleBy(2)	// implicit (&p)

第三種是:如果接收器的實(shí)參是指針*T,形參是T,編譯器會隱式地幫助我們解引用,取到指針實(shí)際指向的變量值:

pptr.Distance(q)	// implicit (*pptr)

nil 也是合法的接收器類型

就像函數(shù)允許 nil 值的指針作為參數(shù)一樣,方法本質(zhì)上也是函數(shù),因此該類型的指針接收器方法可以通過 nil 指針來調(diào)用。

下例是一個(gè)鏈表求和的例子,該例通過調(diào)用鏈表類型的 Sum 方法來對鏈表進(jìn)行求和,由于 nil 指針也可以調(diào)用對應(yīng)類型的方法,因此當(dāng)鏈表到尾時(shí),nil 仍然可以繼續(xù)調(diào)用 Sum 方法,只不過這次調(diào)用會在方法的邏輯中判斷出當(dāng)前指針為 nil,返回 0:

// An IntList is a linked list of integers.
// A nil *IntList represents the empty list.
type IntList struct {Value intTail  *IntList
}
// Sum returns the sum of the list elements.
func (list *IntList) Sum() int {if list == nil {return 0}return list.Value + list.Tail.Sum()
}

通過嵌入結(jié)構(gòu)體來擴(kuò)展類型

下例定義了一個(gè) ColoredPoint 類型,它將 Point 類型作為嵌入加入到了結(jié)構(gòu)體的定義當(dāng)中:

type Point struct { X, Y int64 }
type ColoredPoint struct {PointColor color.RGBA
}

基于結(jié)構(gòu)體內(nèi)嵌的方式,我們可以直接認(rèn)為嵌入的字段就是 ColoredPoint 自己的字段,在使用時(shí)完全不需要指出 Point,ColoredPoint 本身就可以直接訪問 X 和 Y 成員:

var cp ColoredPoint
cp.X = 1
fmt.Println(cp.Point.X)
cp.Point.Y = 2
fmt.Println(cp.Y)

對于 Point 中的方法,我們也有類似的用法,可以把 ColoredPoint 類型當(dāng)作接收器來調(diào)用 Point 里的方法,即使 ColoredPoint 沒有聲明這些方法:

red := color.RGBA{255, 0, 0, 255}
blue := color.RGBA{0, 0, 255, 255}
var p = ColoredPoint{Point{1, 1}, red}
var q = ColoredPoint{Point{5, 4}, blue}
fmt.Println(p.Distance(q.Point)) // "5"
p.ScaleBy(2)
q.ScaleBy(2)
fmt.Println(p.Distance(q.Point)) // "10"

Point 類的方法也被引入了 ColoredPoint,故內(nèi)嵌可以使我們定義字段特別多的復(fù)雜類型,可以先將字段按小類型分組,然后定義小類型的方法,之后再把它們組合起來。

需要注意的是,Point 嵌入在了 ColoredPoint 當(dāng)中,這種關(guān)系不是繼承,也不是子類與父類之間的關(guān)系。ColoredPoint “has” a Point,所以在調(diào)用 Distance 方法時(shí),方法傳入的實(shí)參必須顯式地選擇 ColoredPoint 當(dāng)中的 Point 對象,否則編譯器會報(bào)錯:compile error: cannot use q (ColoredPoint) as Point。

ColoredPoint 不是 Point,但基于內(nèi)嵌,它擁有一個(gè) Point,并且從擁有的 Point 中引入了 Distance 和 ScaleBy 方法。從具體的實(shí)現(xiàn)角度來說,內(nèi)嵌字段會指導(dǎo)編譯器隱式地額外生成方法來對已有的方法進(jìn)行封裝,等價(jià)于:

func (p ColoredPoint) Distance(q Point) float64 {return p.Point.Distance(q)
}func (p *ColoredPoint) ScaleBy(factor float64) {p.Point.ScaleBy(factor)
}

因此,即使我們通過 ColoredPoint 對象調(diào)用內(nèi)嵌的 Point 的方法,在 Point 的方法中我們也無法訪問 ColoredPoint 的成員。

在類型中內(nèi)嵌的匿名字段也可能是一個(gè)命名類型的指針,這種情況下字段和方法會間接地引入到當(dāng)前的類型中。添加這一層間接關(guān)系讓我們可以共享通用的結(jié)構(gòu)并動態(tài)地改變對象之間的關(guān)系。下例的 ColoredPoint 聲明內(nèi)嵌了一個(gè)*Point指針:

type ColoredPoint struct {*PointColor color.RGBA
}p := ColoredPoint{&Point{1, 1}, red}
q := ColoredPoint{&Point{5, 4}, blue}
fmt.Println(p.Distance(*q.Point)) // "5"
q.Point = p.Point	// Now, p and q share the same Point
p.ScaleBy(2)
fmt.Println(*p.Point, *q.Point)	// {2, 2}, {2, 2}

一個(gè) struct 可以定義多個(gè)匿名字段,例如:

type ColoredPoint struct {Pointcolor.RGBA
}

這種類型的值會擁有 Point 和 RGBA 類型的所有方法,以及直接定義在 ColoredPoint 中的方法。當(dāng)編譯器解析一個(gè)選擇器到方法時(shí),比如p.ScaleBy,它會首先去找直接定義在這個(gè)類型當(dāng)中的ScaleBy方法,然后找 ColoredPoint 內(nèi)嵌字段引入的方法,然后去 Point 和 RGBA 的內(nèi)嵌字段繼續(xù)找引入的方法,一直遞歸向下尋找直到找到為止。如果選擇器有二義性的話,編譯器會報(bào)錯,比如你在同一級里有兩個(gè)同名的方法。

下例展示了一個(gè)基于 Go 實(shí)現(xiàn)的非常簡單的 Cache 的 Demo:

var cache = struct {sync.Mutexmapping map[string]string
}{mapping: make(map[string]string),
}func Lookup(key string) string {cache.Lock()v := cache.mapping[key]cache.Unlock()return v
}

該例中,sync.Mutex字段被嵌入到了 struct 當(dāng)中,故其 Lock 和 Unlock 方法也被引入到了 cache 對應(yīng)的匿名結(jié)構(gòu)類型,使得我們可以非常方便地進(jìn)行加鎖和解鎖操作。

方法值和方法表達(dá)式

我們之前使用過的p.Distance(注意,不帶括號,此時(shí)是方法的值)叫做“選擇器”,選擇器會返回一個(gè)方法“值”,即一個(gè)將方法(Point.Distance)綁定到特定接收器變量的函數(shù)。這個(gè)函數(shù)調(diào)用時(shí)不需要指定接收器,因?yàn)橐呀?jīng)在p.Distance中指定p為接收器了,此時(shí)只需要傳入?yún)?shù)即可:

p := Point{1, 2}
q := Point{4, 6}distanceFromP := p.Distance		// p.Distance 獲取方法值, 綁定到 distanceFromP 上
// ?? 此時(shí)已經(jīng)選擇 p 為接收器了
fmt.Println(distanceFromP(q))	// "5"

當(dāng)T是一個(gè)類型時(shí),方法表達(dá)式可能會寫作T.f(*T).f,此時(shí)返回的是一個(gè)函數(shù)的“值”,這種函數(shù)會將第一個(gè)傳入的參數(shù)作為接收器,例如:

p := Point{1, 2}
q := Point{4, 6}
distance := Point.Distance
fmt.Println(distance(p, q))

這一點(diǎn)不難理解,因?yàn)槲以诒酒_頭已經(jīng)提到,Golang 的 Method 實(shí)際上是一種語法糖,它本質(zhì)上是一個(gè)以方法調(diào)用者為第一個(gè)實(shí)參的函數(shù)。因此,類型的方法值就是函數(shù)本身,即:

distance := Point.Distance
// ?? distance 本身是一個(gè)有兩個(gè)形參的函數(shù), 這兩個(gè)形參的類型都是 Point

封裝

“一個(gè)對象的變量或方法對調(diào)用方不可見”被定義為“封裝”,詳細(xì)來說也可以稱為“信息隱藏”。封裝是面向?qū)ο蟮奶匦灾弧?/p>

Go 只有一種可見性手段,那就是大寫首字母的標(biāo)識符會從定義它們的包中被導(dǎo)出,小寫字母則不會導(dǎo)出。這種限制包內(nèi)成員可見性的方式同樣適用于 struct 或一個(gè)類型的方法?;谏鲜鲈?#xff0c;如果我們想對一個(gè)對象進(jìn)行封裝,那么它必須是一個(gè) struct。

下例定義了一個(gè) IntSet 類型,盡管它只有一個(gè)字段,但是由于我們想要對它進(jìn)行封裝,所以必須把這個(gè)單獨(dú)的字段定義在 struct 當(dāng)中:

type IntSet struct {words []uint64
}	// words 是非導(dǎo)出的, 用戶無法直接訪問// ?? 如果我們直接定義為
type IntSet []uint64	// 該方法會使得其他包的用戶直接改變 IntSet 底層的 []uint64

這種基于名字的手段使得在 Golang 語言層面最小的封裝單元是 package,而不是其他語言一樣的類型。一個(gè) struct 類型的字段對同一個(gè)包內(nèi)的所有代碼都有可見性,無論你的代碼是寫在一個(gè)函數(shù)還是一個(gè)方法里。

封裝提供了三個(gè)優(yōu)點(diǎn):

  1. 調(diào)用方不能直接修改對象的變量值,而修改只能通過包的發(fā)布人員對外提供的接口來完成;
  2. 隱藏了實(shí)現(xiàn)的細(xì)節(jié),防止調(diào)用方以來那些可能變化的具體實(shí)現(xiàn),這使得設(shè)計(jì)包的程序員可以在不破壞對外 API 的情況下獲得更多開發(fā)上的自由;
  3. 阻止外部調(diào)用方對對象內(nèi)部的值任意地進(jìn)行修改。

Go 的編程風(fēng)格不禁止導(dǎo)出字段,一旦導(dǎo)出,就無法保證在 API 兼容的前提下去除對已經(jīng)導(dǎo)出字段的導(dǎo)出。

http://www.risenshineclean.com/news/30205.html

相關(guān)文章:

  • 天津平臺網(wǎng)站建設(shè)哪家好如何免費(fèi)建立一個(gè)網(wǎng)站
  • 做網(wǎng)站代理怎么樣成人本科報(bào)考官網(wǎng)
  • wordpress add_filter青島seo網(wǎng)絡(luò)優(yōu)化公司
  • 揚(yáng)州城鄉(xiāng)建設(shè)局網(wǎng)站移動端優(yōu)化
  • 做任務(wù)懸賞網(wǎng)站百度網(wǎng)頁版登錄入口官網(wǎng)
  • 企業(yè)網(wǎng)盤是什么優(yōu)化關(guān)鍵詞排名seo
  • 源碼網(wǎng)站下載網(wǎng)絡(luò)推廣培訓(xùn)去哪里好
  • 無錫大型網(wǎng)站建設(shè)公司谷歌網(wǎng)站優(yōu)化
  • 上海鴻鵠設(shè)計(jì)公司seo頁面內(nèi)容優(yōu)化
  • 安徽做政府網(wǎng)站的企業(yè)日結(jié)app推廣聯(lián)盟
  • 做網(wǎng)站需要公司嗎建網(wǎng)站軟件工具
  • 濟(jì)南網(wǎng)站建設(shè)方案托管福州百度推廣排名優(yōu)化
  • 一個(gè)用戶注冊的網(wǎng)站怎么做網(wǎng)絡(luò)營銷的特點(diǎn)有哪些
  • 微網(wǎng)站的鏈接怎么做的網(wǎng)站推廣蘇州
  • 微信公眾號鏈接的網(wǎng)站怎么做南寧seo網(wǎng)絡(luò)推廣
  • 金融網(wǎng)站開發(fā)文檔百度小說搜索風(fēng)云排行榜
  • 代理機(jī)構(gòu)做的網(wǎng)站找不到人了怎么辦谷歌瀏覽器 安卓下載
  • 企業(yè)網(wǎng)站建設(shè)商城建網(wǎng)站的流程
  • 美食網(wǎng)站怎么做web網(wǎng)站模板
  • 如皋做公司網(wǎng)站杭州關(guān)鍵詞優(yōu)化測試
  • .net網(wǎng)站模板搭建網(wǎng)站需要哪些步驟
  • 公司注冊網(wǎng)上核名多久seo推廣工具
  • web項(xiàng)目開發(fā)seo工作
  • 建材網(wǎng)站建設(shè)功能方案汽車網(wǎng)絡(luò)營銷策劃方案
  • 一個(gè)企業(yè)做網(wǎng)站需要什么資料免費(fèi)下優(yōu)化大師
  • 合肥優(yōu)化網(wǎng)站哪家公司好拼多多關(guān)鍵詞排名查詢工具
  • 企業(yè)網(wǎng)站模板演示seo外包如何
  • 做網(wǎng)站推廣優(yōu)化淘寶排名查詢工具
  • 攝影網(wǎng)站的市場可行性店鋪推廣
  • 建網(wǎng)站的公司大全開創(chuàng)集團(tuán)與百度