函數(shù)的聲明
在 Go 語言中,函數(shù)聲明通用語法如下:
func 函數(shù)名稱( [參數(shù)列表] ) ([返回值列表])
{
執(zhí)行語句
}
1.函數(shù)的聲明以關(guān)鍵詞 func開始 。
- 函數(shù)名和參數(shù)列表一起構(gòu)成了函數(shù)簽名越走。
- 參數(shù)列表定義在
(
和)
之間缓呛,聲明一個參數(shù)的語法采用 參數(shù)名 參數(shù)類型 的方式催享,任意多個參數(shù)采用類似(parameter1 type, parameter2 type) 即(參數(shù)1 參數(shù)1的類型,參數(shù)2 參數(shù)2的類型)
的形式指定。參數(shù)是可選的哟绊,即函數(shù)可以不包含參數(shù)因妙。參數(shù)就像一個占位符,這是參數(shù)被稱為形參票髓,當(dāng)函數(shù)被調(diào)用時攀涵,將具體的值傳遞給參數(shù),這個值被稱為實際參數(shù)洽沟。 - Go函數(shù)支持多返回值以故。 函數(shù)可以有返回值也可以沒有。所以下面這個函數(shù)的聲明也是有效的:
func functionname() {
}
我們以寫一個計算商品價格的函數(shù)為例裆操,輸入?yún)?shù)是單件商品的價格和商品的個數(shù)怒详,兩者的乘積為商品總價,作為函數(shù)的輸出值踪区。
func calculateBill(price int, no int) int {
var totalPrice = price * no // 商品總價 = 商品單價 * 數(shù)量
return totalPrice // 返回總價
}
上述函數(shù)有兩個整型的輸入 price
和 no
昆烁,返回值 totalPrice
為 price
和 no
的乘積,也是整數(shù)類型缎岗。
如果有連續(xù)若干個參數(shù)静尼,它們的類型一致,那么我們無須一一羅列,只需在最后一個參數(shù)后添加該類型鼠渺。*例如鸭巴,price int, no int
可以簡寫為 price, no int
,所以示例函數(shù)也可寫成
func calculateBill(price, no int) int {
var totalPrice = price * no
return totalPrice
}
現(xiàn)在我們已經(jīng)定義了一個函數(shù)拦盹,我們要在代碼中嘗試著調(diào)用它鹃祖。調(diào)用函數(shù)的語法為 functionname(parameters)
。調(diào)用示例函數(shù)的方法如下:
calculateBill(10, 5)
完成了示例函數(shù)聲明和調(diào)用后掌敬,我們就能寫出一個完整的程序惯豆,并把商品總價打印在控制臺上:
func calculateBill(price, no int) int {
var totalPrice = price * no
return totalPrice
}
func main() {
price, no := 90, 6 // 定義 price 和 no,默認(rèn)類型為 int
totalPrice := calculateBill(price, no)
fmt.Println("Total price is", totalPrice) // 打印到控制臺上
}
函數(shù)調(diào)用
如果函數(shù)和調(diào)用不在同一個包(package)內(nèi),需要先通過import關(guān)鍵字將包引入–import “fmt”奔害。函數(shù)Println()就屬于包fmt楷兽。與其他語言不同的是在Go語言中函數(shù)名字的大小寫不僅僅是風(fēng)格,更直接體現(xiàn)了該函數(shù)的是私有函數(shù)還是公有函數(shù)华临。函數(shù)名首字母小寫為private,大寫為public芯杀。
package utiles
func Max(a,b int )(m int){
if a>b {
return a
}
return b
}
Max 函數(shù)如果被其他包調(diào)用 首字母需要大寫
import (
"utiles"
"fmt"
)
func min(a ,b int) (m int){
if a>b {
return b
}
return a
}
func main(){
r := utiles.Max(2,4)
d := min(2,4)
fmt.Println(r,d)
}
多個返回值
Go 語言支持一個函數(shù)可以有多個返回值。
import (
"fmt"
)
func swap(a string,b string)(string,string){
return b,a;
}
func main() {
a,b :=swap("abc","def")
fmt.Println(a,b)
}
如果調(diào)用方調(diào)用了一個具有多返回值的方法雅潭,但是卻不想關(guān)心其中的某個返回值揭厚,用一個下劃線 _ 來忽略這個返回值。如同一個占位符號扶供,如果我們只關(guān)注第一個返回值則可以寫成:
a, _ := swap("hello", "world")
如果關(guān)注第二返回值則可以寫成:
_, b := swap("hello", "world")
函數(shù)參數(shù)
1.值傳遞:調(diào)用函數(shù)時將實際參數(shù)復(fù)制一份傳遞到函數(shù)中筛圆,這樣在函數(shù)中如果對參數(shù)進行修改,將不會影響到實際參數(shù)椿浓。
2.引用傳遞:調(diào)用函數(shù)時將實際參數(shù)復(fù)制一份傳遞到函數(shù)中太援,這樣在函數(shù)中如果對參數(shù)進行修改,將不會影響到實際參數(shù)扳碍。
import (
"fmt"
)
func changeValue(a string){
a = "new value"
}
func change(a *string){
*a = "new value2"
}
func main() {
a := "old value"
changeValue(a)
fmt.Println(a)
change(&a)
fmt.Println(a)
/*
print result:
old value
new value2
*/
}
返回值
從函數(shù)中可以返回一個命名值提岔。一旦命名了返回值,可以認(rèn)為這些值在函數(shù)第一行就被聲明為變量了笋敞。命名返回值作為結(jié)果形參(result parameters)被初始化為相應(yīng)類型的零值碱蒙,當(dāng)需要返回的時候,我們只需要一條簡單的不帶參數(shù)的return語句夯巷。
func getResult(input int) (a int, b int) {
a = 2 * input
b = 3 * input
return
}
func main() {
n,m:= getResult(2);
fmt.Print(n,m)
}
函數(shù)中的 return 語句沒有顯式返回任何值赛惩。由于a 和 b 在函數(shù)聲明中指定為返回值, 因此當(dāng)遇到 return 語句時, 它們將自動從函數(shù)返回。
可變參數(shù)
Go函數(shù)支持不定數(shù)量的參數(shù)的趁餐。為了做到這點喷兼,首先需要定義函數(shù)使其接受變參,類型“…type“本質(zhì)上是一個數(shù)組切片,也就是[]type澎怒,
func funcName(arg ...type) {
}
arg ...type告訴Go這個函數(shù)接受不定數(shù)量的參數(shù)。在下面的例子中參數(shù)的類型全部是int。在函數(shù)體中喷面,變量arg是一個int的slice.
在參數(shù)賦值時可以不用用一個一個的賦值星瘾,可以直接傳遞一個數(shù)組或者切片,特別注意的是在參數(shù)后加上“…”即可惧辈。
import (
"fmt"
)
func min(a...int) int{
if len(a)==0 {
return 0
}
min := a[0]
for _,v :=range a{
if min >v {
min = v
}
}
return min;
}
func main() {
n := min(7,12,3,9,8)
arr := []int {45,27,60,67,18}
r := min(arr...)
m := min(arr[:3]...)
fmt.Print(n,r,m)
}
那么如果函數(shù)的參數(shù)類型不一致需要使用interface{}
import (
"fmt"
"reflect"
)
func diffType(args ...interface{}){
for _, arg :=range args {
fmt.Println(arg)
fmt.Println(reflect.TypeOf(arg))
}
}
func main() {
diffType("test",1,12.5,[5]byte{1,3,4})
}
defer
return 可以返回 函數(shù)執(zhí)行的結(jié)果值琳状,通過關(guān)鍵字 defer 允許我們推遲到函數(shù)返回之前(或任意位置執(zhí)行 return 語句之后)一刻才執(zhí)行某個語句或函數(shù),這樣return不是單純地返回某個值盒齿,同樣可以包含一些操作念逞。
注意:多個defer語句 遵循棧的特征:先進后出
func testDefer() {
defer fmt.Println("hello")
defer fmt.Println("hello v2")
defer fmt.Println("hello v3")
fmt.Println("aaaaa")
fmt.Println("bbbb")
}
func testDefer2() {
var i int = 0
defer fmt.Printf("defer i=%d\n", i)
i= 1000
fmt.Printf("i=%d\n", i)
}
func main() {
testDefer()
testDefer2()
}
匿名函數(shù)
匿名函數(shù)由一個不帶函數(shù)名的函數(shù)聲明和函數(shù)體組成,比如:
func(x边翁,y int) int {
return x + y
}
Go中函數(shù)是值類型翎承,即可以作為參數(shù),又可以作為返回值符匾。
匿名函數(shù)可以賦值給一個變量:
f := func() int {
...
}
定義函數(shù)類型:
type CalcFunc func(x,y int ) int
函數(shù)作為參數(shù):
func Add(x, y int) int {
return x + y
}
func Sub(x, y int) int {
return x - y
}
type CalcFunc func(x,y int ) int
func Operation(x, y int, calcFunc CalcFunc) int {
return calcFunc(x, y)
}
func main() {
sum := Operation(1, 2, Add)
difference := Operation(1, 2, Sub)
fmt.Println(sum,difference)
}
函數(shù)可以作為返回值:
// 第一種寫法
func add(x, y int) func() int {
f := func() int {
return x + y
}
return f
}
// 第二種寫法
func add(x, y int) func() int {
return func() int {
return x + y
}
}
閉包
避免程序運行時異常崩潰
Golang中對于一般的錯誤處理提供了error接口叨咖,對于不可預(yù)見的錯誤(異常)處理提供了兩個內(nèi)置函數(shù)panic和recover。error接口類似于C/C++中的錯誤碼啊胶,panic和recover類似于C++中的try/catch/throw甸各。
當(dāng)在一個函數(shù)執(zhí)行過程中調(diào)用panic()函數(shù)時,正常的函數(shù)執(zhí)行流程將立即終止焰坪,但函數(shù)中之前使用defer關(guān)鍵字延遲執(zhí)行的語句將正常展開執(zhí)行趣倾,之后該函數(shù)將返回到調(diào)用函數(shù),并導(dǎo)致逐層向上執(zhí)行panic流程某饰,直至所屬的goroutine中所有正在執(zhí)行的函數(shù)被終止儒恋。錯誤信息將被報告,包括在調(diào)用panic()函數(shù)時傳入的參數(shù)露乏,這個過程稱為異常處理流程碧浊。
recover函數(shù)用于終止錯誤處理流程。一般情況下瘟仿,recover應(yīng)該在一個使用defer關(guān)鍵字的函數(shù)中執(zhí)行以有效截取錯誤處理流程箱锐。如果沒有在發(fā)生異常的goroutine中明確調(diào)用恢復(fù)過程(調(diào)用recover函數(shù)),會導(dǎo)致該goroutine所屬的進程打印異常信息后直接退出劳较。
對于第三方庫的調(diào)用驹止,在不清楚是否有panic的情況下,最好在適配層統(tǒng)一加上recover過程观蜗,否則會導(dǎo)致當(dāng)前進程的異常退出臊恋,而這并不是我們所期望的。
簡單的實現(xiàn)如下:
func thirdPartyAdaptedHandler(...) {
defer func() {
err := recover()
if err != nil {
fmt.Println("some exception has happend:", err)
}
}()
...
}