Go入门系列(四)函数
目录:
一、基础
二、匿名
三、接口
四、错误处理
一、基本函数
Go语言的函数声明格式为:
func function_name( [parameter list] ) [return_types] {
函数体
}
func:函数由 func 开始声明
function_name:函数名称,函数名和参数列表一起构成了函数签名。
parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
函数体:函数定义的代码集合。
基本的声明、调用、传参方式如下:
func main() {
a,b:=who("rabbit",18)
fmt.Println(a,b)
}
func who(name string, age int) (string, int) {
return name, age
}
#输出
rabbit 18
二、匿名函数
匿名函数是指不需要定义函数名的一种函数实现方式,由一个不带函数名的函数声明和函数体组成,上一章我们提到过,函数类型是变量的一种,所以匿名函数往往以变量方式传递。
可以使用变量传递然后赋值:
func main() {
who:=func (name string, age int) (string, int) {
return name, age
}
a,b:=who("rabbit",18)
fmt.Println(a,b)
}
#输出
rabbit 18
也可以直接赋值:
func main() {
a,b:=func (name string, age int) (string, int) {
return name, age
}("rabbit",18)
fmt.Println(a,b)
}
#输出
rabbit 18
其使用方式类似C语言中的回调函数。
另外在此我们再回顾一个概念:闭包函数,声明在一个函数中的函数,叫做闭包函数,而Go语言中的匿名函数机制,完美符合闭包函数的设定。
其实闭包函数和匿名函数的概念在很多语言中很难区分,至于我的理解,其实他俩并没有直接关系,至少匿名函数的使用这个行为构成了闭包。
多说一句很多其他语言的老玩家都深有感触,过度使用闭包容易造成内存的浪费,但这一点再Go语言中把控的比较严格,笔者作为入门新人不做太多评论,日后再来反馈。
三、接口
接口,在软件开发流程中对团队契合度来讲是个极其重要的地方,简单说调用方和实现方均需要遵守的一种协议,大家按照统一的方法命名参数类型和数量来协调逻辑处理的过程。
然后对于我们个人开发来讲,也会方便自己对项目代码进行合理的维护和功能拓展。
接口格式规则为:
type 接口类型名 interface{
方法名1( 参数列表1 ) 返回值列表1
方法名2( 参数列表2 ) 返回值列表2
…
}
我们通过样例来实现一个反馈用户信息的功能:
package main
import (
"fmt"
"time"
)
type UserInfo interface {
login(name string)
info(name string,age int,city string)
}
type People struct {
name string
age int
city string
}
func (people People) login(name string) {
fmt.Printf("%s您好,欢迎登陆\n登录时间为:%s\n",name,time.Now())
}
func (people People) info(name string,age int,city string) {
fmt.Printf("您的个人资料为:姓名:%s 年龄:%d 城市:%s\n",name,age,city)
}
func main() {
var userinfo UserInfo
userinfo = new(People)
userinfo.login("rabbit")
userinfo.info("rabbit",18,"Shandong")
}
#输出
rabbit您好,欢迎登陆
登录时间为:2020-03-14 16:26:35.2352272 +0800 CST m=+0.003989101
您的个人资料为:姓名:rabbit 年龄:18 城市:Shandong
此外接口还允许嵌套使用,将多个接口进行组合,作为入门系列我们简单提及暂不做代码展开。
四、错误处理
错误处理,区别与其他语言中的异常处理。其他语言的老玩家不得不承认一个事实,所谓的异常捕捉机制,倒不如说异常屏蔽机制,异常依然发生了,只不过通过该机制进行了屏蔽不影响程序运行。
Go语言的设计者认为其他语言的异常机制已被过度使用,上层逻辑需要为函数发生的异常付出太多的资源,同时,如果函数使用者觉得错误处理很麻烦而忽略错误,那么程序将在不可预知的时刻崩溃。
Go语言希望开发者将错误处理视为正常开发必须实现的环节,正确地处理每一个可能发生错误的函数,同时,Go语言使用返回值返回错误的机制,也能大幅降低编译器、运行时处理错误的复杂度,让开发者真正地掌握错误的处理。
综上所述,Go语言不会为你屏蔽异常,它需要你正视可能出现的问题并手动去给出异常提示。
其二,为什么错误处理放到了这一章节,因为Go语言错误处理的机制其本质正是接口。
error类型是一个接口类型,这是它的定义:
type error interface {
Error() string
}
后边我们在使用官方包的时候会接触很多已经定义好的错误提示,在这里我们先尝试自定义错误,我们可以在编码中通过实现 error 接口类型来生成错误信息,函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息。
这么说可能有点抽象,我们尝试继续升级我们上面的例子,比如加入年龄错误检测。
package main
import (
"errors"
"fmt"
"time"
)
type UserInfo interface {
login(name string)
info(name string,age int,city string)
}
func AgeTure(age int) (int, error) {
if age < 0 || age > 200 {
return 0, errors.New("请正确输入您的年龄!")
}else {
return 1, nil
}
}
type People struct {
name string
age int
city string
}
func (people People) login(name string) {
fmt.Printf("%s您好,欢迎登陆\n登录时间为:%s\n",name,time.Now())
}
func (people People) info(name string,age int,city string) {
result, err:= AgeTure(age)
if err != nil {
fmt.Println(result,err)
}else {
fmt.Printf("您的个人资料为:姓名:%s 年龄:%d 城市:%s\n",name,age,city)
}
}
func main() {
var userinfo UserInfo
userinfo = new(People)
userinfo.login("rabbit")
userinfo.info("rabbit",1988,"Shandong")
}
#输出
rabbit您好,欢迎登陆
登录时间为:2020-03-15 12:40:52.8176698 +0800 CST m=+0.003991401
0 请正确输入您的年龄!
我们键入了错误的年龄,触发了自定义错误提示,其中nil
是go语言中预先定义的标识符,可看作是其他语言中的null(NULL),我们可以直接使用nil,而不用声明它。
另外多说一句,go其实确实保留了类似其他语言的异常处理机制,但有区别,但是官方的目的是为了调试,而非让你掩盖异常,所以我们选择适应这个语言,仅在此简单提及:panic 与 recover 是 Go 的两个内置函数,这两个内置函数用于处理 Go 运行时的错误,panic 用于主动抛出错误,recover 用来捕获 panic 抛出的错误。