Skip to content

函数调用

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") 
}

参数解析

code

  • 请注意 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()

返回值

code

  • 多值返回
  • 函数可以返回任意数量的返回值
  • 命名返回值
  • Go 的返回值可被命名,它们会被视作定义在函数顶部的变量。
  • 返回值的名称应当具有一定的意义,它可以作为文档使用。
  • 没有参数的 return 语句返回已命名的返回值。也就是直接返回。
  • 调用者忽略部分返回值
result, _ = strconv.Atoi(origStr)

Init 函数

code

  • 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)

code

  • 函数作为参数传入其它函数,并在其他函数内部调用执行
    • 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{}
    }
}

传值还是传指针

code

  • Go 语言只有一种规则-传值
  • 函数内修改参数的值不会影响函数外原始变量的值
  • 可以传递指针参数将变量地址传递给调用函数,Go 语言会复制该指针作为函数内的地址,但指向同一地址
  • 思考:当我们写代码的时候,函数的参数传递应该用 struct 还是 pointer?

接口

code

  • 接口定义一组方法集合
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 的引用不会出现空指针

反射机制

code

  • 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 编解码

code

  • 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)
    } 
}