一起聊聊Go語言中的語法糖的使用
前言
由于工作變動,我現(xiàn)在已經開始使用Golang了。用了一段時間之后,我發(fā)現(xiàn)Golang(后面簡稱Go)中的語法糖還蠻多的,有些語法糖還讓會讓人很懵逼。那么接下來,讓我以一個曾經的 Java CURD boy,來說一說 Go 中的語法糖。
進入正題
至于什么是語法糖,名詞解釋我就不解釋了,老司機自然是懂,新手司機想了解的可以去百度問一下。閑話少說我們直接開講。
可變長參數(shù)
Go語言允許一個函數(shù)把任意數(shù)量的值作為參數(shù),Go語言內置了... 操作符,在函數(shù)的最后一個形參才能使用...操作符,使用它必須注意如下事項
- 可變長參數(shù)必須在函數(shù)列表的最后一個;
- 把可變長參數(shù)當
切片來解析,可變長參數(shù)沒有沒有值時就是個空切片 - 可變長參數(shù)的類型必須相同
func test(a int, b ...int) {
fmt.Println("a=", a, ",b=", b, ",b的類型=", reflect.TypeOf(b))
return
}輸出結果如下:
a= 1 ,b= [] ,b的類型= []int
為啥說可變長參數(shù)的值用切片來解析,而不是數(shù)組。為什么是這樣有興趣的朋友可以思考一下
可變長參數(shù)這個語法糖,不是Go獨有的,Java中也有,不同的是Java是通過數(shù)組實現(xiàn)此語法糖的。從實際開發(fā)經驗來看,這個語法糖我在使用Java開發(fā)時,貌似一次都沒有用過,用Go開發(fā)的時候我用的次數(shù)還挺多的,具體在什么地方用,后面有機會我再說說它是如何使用的。
聲明不定長數(shù)組
我么都知道數(shù)組長度是固定的,所以在聲明數(shù)組的時候都要指定長度,Go里提供了一種偷懶的聲明方式,即使用...操作符聲明數(shù)組時,我們只管填充元素值,其他的由Go編譯器來處理。
// Go的實現(xiàn):數(shù)組長度是4,等同于 a := [4]{1, 2, 3, 4}
a := [...]int{1, 2, 3, 4}
這個Java中有實現(xiàn),而且感覺比Go的還簡單,具體如下:
// Java的實現(xiàn):數(shù)組長度是4
int[] x = {1,2,3,4};
在我短暫的職業(yè)生涯中,無論我使用Java還是Go開發(fā)的時候,數(shù)組使用的頻率都是比較少的。
ps 我發(fā)現(xiàn)這個...好像也算是一個語法糖
... 操作符
...這個叫啥名字,我也沒有找到官方的叫法。但是我發(fā)現(xiàn)在Go實際的開發(fā)過程中用的地方還蠻多的。
- 函數(shù)的參數(shù)聲明。 如:
func funcName(nums ...int),在函數(shù)的方法體內,nums作為一個切片[]int來使用,這個上面已經提到了。 - 傳參時列表打散。 如:
params = []int{1,2,3},調用某個有三個參數(shù)的方法func ThreeParamFunc(a, b, c int)時可以ThreeParamFunc(params...)。三個點...在JavaScript中的名叫擴展運算符,是在ES6中新增加的內容,它可以在函數(shù)調用/數(shù)組構造時,將數(shù)組表達式或者string在語法層面展開;還可以在構造字面量對象時將對象表達式按照key-value的方式展開,例如:
// 數(shù)組
var number = [1,2,3,4,5,6]
console.log(...number) //1 2 3 4 5 6
//對象
var man = {name:'蔡',height:180}
console.log({...man}) / {name:'蔡',height:180}
所以我覺得在Go里面在這種情況下,我們也可以稱...為擴展運算符。
- 聲明不定長數(shù)組。 如果元素指定,那么可以不必顯式聲明數(shù)組長度,可以根據(jù)元素個數(shù)推斷,如:
arr := [...]int{1,2,3},這個上面已經提到了。 - 在 go 命令行中,被當做包列表的通配符。如:
$ go test ./...這條命令會執(zhí)行當前目錄及子目錄下的所有包測試文件。
切片循環(huán)
在Go中提供了for range語法來快速迭代對象。數(shù)組、切片、字符串、map、channel等等類型都可以使用這種方式進行遍歷,總結起來有以下幾種形式:
只遍歷不關心數(shù)據(jù),適用于切片、數(shù)組、字符串、map、channel
for range T {}
遍歷獲取索引或數(shù)組,切片,數(shù)組、字符串就是索引,map就是key,channel就是數(shù)據(jù)
for key := range T{}
遍歷獲取索引和數(shù)據(jù),適用于切片、數(shù)組、字符串,第一個參數(shù)就是索引,第二個參數(shù)就是對應的元素值,map 第一個參數(shù)就是key,第二個參數(shù)就是對應的值;
for key, value := range T{}
其實在實際開發(fā)中,我們會大概率會遇到遍歷map時,只關心map中的數(shù)據(jù),不關心key的情況。這個時候我們就是使用最后一種方式,這個key聲明了但是沒有用,Go這個時候就會提示一個語法錯誤key沒有使用,那我們只好使用Go的另外一個語法糖_忽略標識符(就是一個下劃線)忽略key,具體如下:
for _, value := range T{}
在Java中循環(huán)map的方式有很多種,但有一點就是,開發(fā)者可以使用keySet()、values()選擇遍歷key或者value。
// 打印鍵集合
for (String key : map.keySet()) {
System.out.println(key);
}
// 打印值集合
for (String value : map.values()) {
System.out.println(value);
}
另外注意一點,在Go中如果一個切片是nil的時候,我們對他進行遍歷或者append操作的時候,是不會出現(xiàn)報錯的,這一點很不錯,省的像用Java時遍歷對象需要判斷他是否為null。
func main() {
temp := make([]int, 0)
temp = nil
for _, val := range temp {
fmt.Println("val=", val)
}
temp = append(temp, 1)
fmt.Println("val=", temp)
}
上述操作都是不會報錯的,大家放心食用!
忽略變量、字段或者導包
這個前面提到了一點,使用_忽略變量。在Go中還有其他幾種常見的場景,具體如下:
json序列化忽略某個字段 我們都會對struct做序列化操作,但有些時候我們想要json里面的某些字段不參加序列化,Go語言的結構體提供標簽功能,在結構體標簽中使用 - 操作符就可以對不需要序列化的字段做特殊處理,使用如下:
type Item struct{
Id uint32 `json:"id"`
Name string `json: "name"`
Password string `json: "-"`
}
這個Java中也有類似的實現(xiàn),只要在Java類的屬性前加上transient關鍵字修飾即可。當然在將Java類序列化成json時可以使用對應的注解,這里我就不細說了。
json序列化忽略空值字段 使用json.Marshal進行序列化時不會忽略struct中的空值(這里說的空值包含空字符串和nil),默認輸出字段的類型零值(string類型零值是"",指針類型的零值是nil),如果我們想在序列化時忽略掉這些沒有值的字段時,可以在結構體標簽中中添加omitempty tag。
type Item struct{
Id uint32 `json:"id"`
Name string `json: "name,omitempty"`
Password string `json: "-"`
}
這里說一下,在Java里類型分為基本類型和包裝類型,Java類初始化的時候屬性為基本類型如果沒有賦予初始值,默認值是0。包裝類型聲明時沒有賦值的話的初始值為null。Go中初始化時沒有賦值的變量的默認值如下:
- 布爾類型的默認為false
- 數(shù)值類型的默認為0
- 字符串類型的默認為空字符串""
- 指針類型、函數(shù)、接口、切片、通道和map默認值為nil
這樣看來Java和Go這個場景下處理方式,有相似和不同之處,大家開發(fā)的時候要注意,由Java轉Go的同學開發(fā)時,千萬別搞混了。
短變量聲明
在強類型語言中,聲明一個變量都需要指定變量的類型??赡苷Z言的開發(fā)者覺得這樣做對開發(fā)者不太友好,就搞了個變量聲明不用指定類型的語法糖,其實這個玩意說起來就是類型推導(Java8之后的版本貌似已經有了),開發(fā)者只管定義變量,類型由語言編譯器來處理。
a := 10 #等用于 var a int = 10 #或者是 b:=fucName()
怎么說呢?這樣有好處也有壞處,定義變量的人省事了,使用變量的人可能就懵逼了。就像這種場景b:=fucName(),這個 變量b是啥類型,這個時候你只能點擊函數(shù)內,看函數(shù)的返回值類型是啥,才能確定變量b是啥類型。
我之前寫過幾年的PHP,后來轉了Java,再到現(xiàn)在寫Go。我發(fā)現(xiàn)各種開發(fā)語言都在進步,而且還相互模仿,PHP中函數(shù)之前不用指定形參類型,PHP8中好像可以指定形參類型了。總之就是強弱類型的語言在相互靠攏。
另類的返回值
在Go語言中,允許您使用return語句從一個函數(shù)返回多個值。換句話說,在函數(shù)中,單個return語句可以返回多個值。返回值的類型類似于參數(shù)列表中定義的參數(shù)的類型。
func func1(a string, b int) int {
fmt.Println("func1------------")
fmt.Println("a1 = ", a)
fmt.Println("b1 = ", b)
c := 100
return c
}可以這樣寫:返回多個返回值,形參命名
func func2(a string, b int) (int, int) {
fmt.Println("func2------------")
fmt.Println("a2 = ", a)
fmt.Println("b2 = ", b)
return 12, 33
}
可以這樣寫:返回多個返回值,形參匿名
func func3(a string, b int) (int, int) {
fmt.Println("func3------------")
fmt.Println("a2 = ", a)
fmt.Println("b2 = ", b)
return 12, 33
}
如果一個函數(shù)要返回多個值,在Java中可以使用定義一個新的類來承載返回值,或者偷個懶使用map來接也是可以的。go支持多個返回值就我個人來說還是支持的。其實說到這里,多個返回值的各種形式都能理解。直到有一天我在翻看gorm的Open方法源碼發(fā)現(xiàn)了奇怪的地方,代碼位置信息:gorm.io/gorm@v1.23.4/gorm.go:116 ,節(jié)選部分代碼如下:
func Open(dialector Dialector, opts ...Option) (db *DB, err error) {
config := &Config{}
if d, ok := dialector.(interface{ Apply(*Config) error }); ok {
if err = d.Apply(config); err != nil {
return
}
}
# 省略此處無用代碼
db = &DB{Config: config, clone: 1}
db.callbacks = initializeCallbacks(db)
# 省略此處無用代碼
preparedStmt := &PreparedStmtDB{
ConnPool: db.ConnPool,
Stmts: map[string]Stmt{},
Mux: &sync.RWMutex{},
PreparedSQL: make([]string, 0, 100),
}
db.cacheStore.Store(preparedStmtDBKey, preparedStmt)
# 省略此處無用代碼
db.Statement = &Statement{
DB: db,
ConnPool: db.ConnPool,
Context: context.Background(),
Clauses: map[string]clause.Clause{},
}
# 省略此處無用代碼
return
}這就是文章開頭提到的讓人懵逼的語法糖,我當時看到這段代碼時,我心中暗想這個是什么TM操作,竟然這樣也行,這樣竟然沒有報錯…… 我來點出其中的問題,就是return關鍵字處并沒有返回db和error變量。我把上述代碼在簡化一下,用最簡單的方式列出來,如下:
func func4(a string, b int) (r1 int, r2 int) {
fmt.Println("func4------------")
//r1 r2輸入fool3的形參,初始化默認的值是0
//r1 r2 作用域空間是 func4 整個函數(shù)體的{}空間
fmt.Println("r1 = ", r1)
fmt.Println("r2 = ", r2)
r1 = b * 2
r2 = 2000
return
}上述這種方式其實本質上來說是和前面幾種方式一樣,只是在不知道這種約定的話,會讓人難以理解。知道這個算是Go中的小tips之后,咱也不知道Go為啥要這么做,只是覺得有點懵,我只是覺得在Java中絕對不會出現(xiàn)這種情況。但是在Go中也許是設計Go的大佬們覺得這樣做可以省掉聲明變量r1和r2的時間,畢竟大佬們的時間都很寶貴。
總結
本文介紹了一些Go語言中的語法糖,當然并不全面,應該還有其他的沒有介紹,希望大家能夠看完本篇文章后,能了解并掌握,并能在實際開發(fā)中運用到,當然其中的函數(shù)多返回值的懵逼寫法,就由大家自行判斷是用還是不用了。
以上就是一起聊聊Go語言中的語法糖的使用的詳細內容,更多關于Go語言 語法糖的資料請關注腳本之家其它相關文章!
相關文章
Golang實現(xiàn)異步上傳文件支持進度條查詢的方法
這篇文章主要介紹了Golang實現(xiàn)異步上傳文件支持進度條查詢的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-10-10

