是时候开始钻研函数和办法了。。。
函数
通过函数,能够把开发工作分解成一个个小的单元,这些小单元能够被其余单元复用,进而进步开发效率、升高代码重合度。
1. 函数申明
func funcName(params) result {body}
- 关键字 func
- 函数名字 funcName
- 函数的参数 params,用来定义形参的变量名和类型
- result 是返回的函数值,用于定义返回值的类型,如果没有能够省略
- body 就是函数体,能够在这里写函数的代码逻辑
写一个计算两数相加的函数:
package main
import ("fmt")
// 计算两值之和
// 变量名称在前,变量类型在后
// 变量名称叫做参数名称,也就是函数的形参
func addTwoNum(a int, b int) int{return a + b}
func main(){fmt.Println(addTwoNum(12, 21))
}
运行后果为:
2. 多值返回
Go 语言的函数能够返回多个值,也就是多值返回
第一个值返回函数的后果,第二个值返回函数出错的信息
package main
import (
"errors"
"fmt"
)
// 计算两值之和,如果为正数就返回谬误
func addTwoNum(a int, b int) (int, error) {
if a < 0 || b < 0 {return 0, errors.New("a 或者 b 不能为正数")
}
return a + b, nil
}
func main() {
// 获取后果和错误信息
a, err := addTwoNum(-12, 21)
if err != nil {fmt.Println(err)
} else {fmt.Println("计算结果为:", a)
}
}
运行后果为:
3. 命名返回参数
函数的返回值也能够有变量名称,这个并不罕用,理解一下
革新函数为:
package main
import (
"errors"
"fmt"
)
// 计算两值之和,如果为正数就返回谬误
func addTwoNum(a int, b int) (sum int, err error) {
if a < 0 || b < 0 {
// 这里依照失常进行返回
return 0, errors.New("a 或者 b 不能为正数")
}
// 这里依照返回值给相干参数赋值,return 前面不须要任何参数
sum = a + b
err = nil
return
}
func main() {
// 获取后果和错误信息
a, err := addTwoNum(-12, 21)
if err != nil {fmt.Println(err)
} else {fmt.Println("计算结果为:", a)
}
}
运行后果为:
4. 可变参数
可变参数,就是函数的参数数量是可变的
如果函数中既有一般参数又有可变参数,那么可变参数肯定要放到参数列表的开端
次要就是在参数类型后面增加三个点
例如:
// 没有参数
fmt.Println()
// 一个参数
fmt.Println("zhouzhaodong")
// 两个参数
fmt.Println("zhouzhaodong","xiaoqiang")
咱们写一个计算所有数字之和的函数:
package main
import ("fmt")
// 计算所有值之和
func addAllNum(params ...int) int {
sum := 0
for _, i := range params {sum += i}
return sum
}
func main() {fmt.Println("计算结果为:", addAllNum(1, 2, 3, 3, 3, 4, 5, 9))
}
运行后果为:
5. 包级函数
不论是自定义的函数
还是咱们应用到的函数,都会隶属一个包
也就是 package
不同包的函数要被调用,那么函数的作用域必须是私有的,也就是 函数名称的首字母要大写
- 函数名称首字母小写代表公有函数,只有在同一个包中才能够被调用
- 函数名称首字母大写代表私有函数,不同的包也能够调用
- 任何一个函数都会从属于一个包
6. 匿名函数和闭包
匿名函数就是没有名字的函数
package main
import ("fmt")
func main() {sum := func(a, b int) int {return a + b}
fmt.Println("计算结果为:", sum(1, 2))
}
运行后果为:
在函数中 再定义函数 (函数嵌套),定义的这个匿名函数,也能够称为 外部函数
更重要的是,在函数内定义的外部函数,能够应用内部函数的变量等,这种形式也称为 闭包
办法
办法必须要有一个 接收者 ,这个接收者是一个类型
这样办法就和这个类型绑定在一起,成为 这个类型的办法
接收者的定义和一般变量、函数参数等一样
后面是变量名,前面是接收者类型
package main
import ("fmt")
// 定义一个新的类型,该类型等价于 uint
// 能够了解为 uint 的重命名
type Age uint
// 定义一个办法,参数就是 Age
func (age Age) String() {fmt.Println("the age is", age)
}
func main() {age := Age(25)
age.String()}
运行后果为:
接收者就是函数和办法最大的不同
1. 值类型接收者和指针类型接收者
定义的办法的接收者类型是指针,所以咱们对指针的批改是无效的,如果不是指针,批改就没有成果,如下所示:
package main
import ("fmt")
// 定义一个新的类型,该类型等价于 uint
// 能够了解为 uint 的重命名
type Age uint
// 定义一个办法,参数就是 Age
func (age Age) String() {fmt.Println("the age is", age)
}
// 定义一个办法,参数就是 Age 指针
func (age *Age) Modify() {*age = Age(30)
}
func main() {age := Age(25)
age.String()
// 批改 age 的值
age.Modify()
age.String()}
运行后果为:
提醒:在调用办法的时候,传递的接收者实质上都是正本,只不过一个是这个值正本,一是指向这个值指针的正本。指针具备指向原有值的个性,所以批改了指针指向的值,也就批改了原有的值。咱们能够简略地了解为值接收者应用的是值的副原本调用办法,而指针接收者应用理论的值来调用办法。
这就是 Go 语言编译器帮咱们主动做的事件:
- 如果应用一个值类型变量调用指针类型接收者的办法,Go 语言编译器会主动帮咱们取指针调用,以满足指针接收者的要求。
- 如果应用一个指针类型变量调用值类型接收者的办法,Go 语言编译器会主动帮咱们解援用调用,以满足值类型接收者的要求。
总之,办法的调用者,既能够是值也能够是指针,不必太关注这些,Go 语言会帮咱们主动本义,大大提高开发效率,同时防止因不小心造成的 Bug。
不论是应用值类型接收者,还是指针类型接收者,要先确定你的需要:在对类型进行操作的时候是要扭转以后接收者的值,还是要创立一个新值进行返回?这些就能够决定应用哪种接收者。
集体博客地址:
http://www.zhouzhaodong.xyz/