函数调用
Main 函数
- 每个 Go 语言程序都应该有个 main package
- Main package 里的 main 函数是 Go 语言程序入口
package main
func main() {
args := os.Args
if len(args) != 0 {
println("Do not accept any argument")
os.Exit(1)
}
println("Hello world")
}
参数解析
- 请注意 main 函数与其他语言不同,没有类似 java 的 []string args 参数
- Go 语言如何传入参数呢?
- 方法1(直接读取):
- fmt.Println("os args is:", os.Args)
- 方法2(通过 flag 解析参数):
- name := flag.String("name", "world", "specify the name you want to say hi")
- flag.Parse()
- 方法1(直接读取):
返回值
- 多值返回
- 函数可以返回任意数量的返回值
- 命名返回值
- Go 的返回值可被命名,它们会被视作定义在函数顶部的变量。
- 返回值的名称应当具有一定的意义,它可以作为文档使用。
- 没有参数的 return 语句返回已命名的返回值。也就是直接返回。
- 调用者忽略部分返回值
result, _ = strconv.Atoi(origStr)
Init 函数
- Init 函数:会在包初始化时运行
- 谨慎使用 init 函数
- 当多个依赖项目引用统一项目,且被引用项目的初始化在 init 中完成,并且不可重复运行时,会导致启动错误
package main
var myVariable = 0
func init() {
myVariable = 1
}
传递变长参数
Go 语言中的可变长参数允许调用方传递任意多个相同类型的参数 - 函数定义
func append(slice []Type, elems ...Type) []Type
- 调用方法
myArray := []string{}
myArray = append(myArray, "a","b","c")
内置函数
| 函数名 | 作用 |
|---|---|
| close | 管道关闭 |
| len,cap | 返回数组、切片,Map 的长度或容量 |
| new, make | 内存分配 |
| copy, append | 操作切片 |
| panic, recover | 错误处理 |
| print, println | 打印 |
| complex, real, imag | 操作复数 |
回调函数(Callback)
- 函数作为参数传入其它函数,并在其他函数内部调用执行
- strings.IndexFunc(line, unicode.IsSpace)
- Kubernetes controller的leaderelection 示例:
func main() {
DoOperation(1, increase)
DoOperation(1, decrease)
}
func increase(a, b int) {
println(“increase result is:”, a+b)
}
func DoOperation(y int, f func(int, int)) {
f(y, 1)
}
func decrease(a, b int) {
println("decrease result is:", a-b)
}
闭包
- 匿名函数
- 不能独立存在
- 可以赋值给其他变量
- x:= func(){}
- 可以直接调用
- func(x,y int){println(x+y)}(1,2)
- 可作为函数返回值
- func Add() (func(b int) int)
- 使用场景 defer func() { if r := recover(); r != nil { println("recovered in FuncX") } }()
方法
- 方法:作用在接收者上的函数
- func (recv receiver_type) methodName(parameter_list) (return_value_list) - 使用场景
- 很多场景下,函数需要的上下文可以保存在receiver属性中,通过定义 receiver 的方法,该方法可以直接访问 receiver 属性,减少参数传递需求
// StartTLS starts TLS on a server from NewUnstartedServer.
func (s *Server) StartTLS() {
if s.URL != “” {
panic(“Server already started”)
}
if s.client == nil {
s.client = &http.Client{Transport: &http.Transport{}
}
}
传值还是传指针
- Go 语言只有一种规则-传值
- 函数内修改参数的值不会影响函数外原始变量的值
- 可以传递指针参数将变量地址传递给调用函数,Go 语言会复制该指针作为函数内的地址,但指向同一地址
- 思考:当我们写代码的时候,函数的参数传递应该用 struct 还是 pointer?
接口
- 接口定义一组方法集合
type IF interface {
Method1(param_list) return_type
}
- 适用场景:Kubernetes 中有大量的接口抽象和多种实现
- Struct 无需显示声明实现 interface,只需直接实现方法
- Struct 除实现 interface 定义的接口外,还可以有额外的方法
- 一个类型可实现多个接口(Go 语言的多重继承)
- Go 语言中接口不接受属性定义
- 接口可以嵌套其他接口
type IF interface {
getName() string
}
type Human struct {
firstName, lastName string
}
func (h *Human) getName() string {
return h.firstName + "," + h.lastName
}
type Car struct {
factory, model string
}
func (c *Car) getName() string {
return c.factory + "-" + c.model
}
func main() {
interfaces := []IF{}
h := new(Human)
h.firstName = "first"
h.lastName = "last"
interfaces = append(interfaces, h)
c := new(Car)
c.factory = "benz"
c.model = "s"
interfaces = append(interfaces, c)
for _, f := range interfaces {
fmt.Println(f.getName())
}
}
注意事项
- Interface 是可能为 nil 的,所以针对 interface 的使用一定要预先判空,否则会引起程序 crash(nil panic)
- Struct 初始化意味着空间分配,对 struct 的引用不会出现空指针
反射机制
- reflect.TypeOf ()返回被检查对象的类型
- reflect.ValueOf()返回被检查对象的值
- 示例
myMap := make(map[string]string, 10)
myMap["a"] = "b"
t := reflect.TypeOf(myMap)
fmt.Println("type:", t)
v := reflect.ValueOf(myMap)
fmt.Println("value:", v)
基于 struct 的反射
// struct
myStruct := T{A: "a"}
v1 := reflect.ValueOf(myStruct)
for i := 0; i < v1.NumField(); i++ {
fmt.Printf("Field %d: %v\n", i, v1.Field(i))
}
for i := 0; i < v1.NumMethod(); i++ {
fmt.Printf("Method %d: %v\n", i, v1.Method(i))
}
// 需要注意 receive 是 struct 还是指针
result := v1.Method(0).Call(nil)
fmt.Println("result:", result)
Go 语言中的面向对象编程
- 可见性控制
- public - 常量、变量、类型、接口、结构、函数等的名称大写
- private - 非大写就只能在包内使用
- 继承
- 通过组合实现,内嵌一个或多个 struct
- 多态
- 通过接口实现,通过接口定义方法集,编写多套实现
Json 编解码
- Unmarshal: 从 string 转换至 struct
func unmarshal2Struct(humanStr string)Human {
h := Human{}
err := json.Unmarshal([]byte(humanStr), &h)
if err != nil {
println(err)
}
return h
}
- Marshal: 从 struct 转换至 string
func marshal2JsonString(h Human) string {
h.Age = 30
updatedBytes, err := json.Marshal(&h)
if err != nil {
println(err)
}
return string(updatedBytes)
}
- json 包使用 map[string]interface{} 和 []interface{} 类型保存任意对象
- 可通过如下逻辑解析任意 json
var obj interface{}
err := json.Unmarshal([]byte(humanStr), &obj)
objMap, ok := obj.(map[string]interface{})
for k, v := range objMap {
switch value := v.(type) {
case string:
fmt.Printf("type of %s is string, value is %v\n", k, value)
case interface{}:
fmt.Printf("type of %s is interface{}, value is %v\n", k, value)
default:
fmt.Printf("type of %s is wrong, value is %v\n", k, value)
}
}