Yayako's Blog

「Shooting for the stars when I couldn't make a killing.」

仅作java2go的简单记录,有错误烦请提出

合集整理:Java转Go的学习-合集

demo已上传git:https://gitee.com/yayako/go-learning.git

函数

碎碎念:回参和入参顺序跟java反着来一开始是真的难受

函数的基本使用

具体格式

1
2
3
func 函数名(入参) (返回结果) {
// TODO
}

普通的一个定义

1
2
3
func getTwoSum(a int, b int) int {
return a + b
}

可变参数

1
2
3
4
5
6
7
func getSum(a ...int) int {
sum := 0
for _, i := range a {
sum += i
}
return sum
}

类型简写:如果入参类型相同,可以简写入参的数据类型

1
2
3
func getSub(a /* int */, b int) int {
return a - b
}

支持多返回值

1
2
3
func getSumAndSub(a, b int) (int, int) {
return a + b, a - b
}

返回值命名:函数定义时可以给返回值名命,并在函数体中直接使用这些变量并return

1
2
3
4
5
func getMultiAndDiv(a int, b int) (multi, div int) {
multi = a * b
div = a / b
return multi, div
}

注意,如果在return时改变了命名返回值的顺序(如上例中修改为 return div, multi ),返回的值的顺序也会改变。

函数作为参数传递
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
type calccalculate func(int, int) int

func calculate(a, b int, f calc /* 此处 calculate 等同于 func(int, int) int */) int {
return f(a, b)
}

func main() {
// 函数作为参数
fmt.Println(calculate(1, 1, getSub))
// 匿名函数作为参数
fmt.Println(calculate(1, 1, func(a int, b int) int {
return a + b - a*b/a
}))
// 函数作为回参
fmt.Println(calculate(1, 1, funcChoose("/")))
}

func funcChoose(s string) calc {
switch s {
case "+":
return getTwoSum
case "-":
return getSub
case "*": // 匿名函数作为返回结果
return func(a int, b int) int {
return a * b
}
case "/": // 匿名函数作为返回结果
return func(a int, b int) int {
return a / b
}
default:
return nil
}
}
init()

go语言程序执行时会自动触发包内 init() 函数的调用,该函数无参数、返回值,在执行时自动被调用执行,不能在代码中被主动调用。

包内函数执行顺序:全局声明 -> init() -> main()

当导入多个包时,最后导入的包会最先初始化并调用其 init() 函数。

1
2
3
func init() {
fmt.Println("ex11:init")
}

结构体自定义函数

1
2
3
func (实例名 实例类型) 方法名()  {
// 方法体
}

假设有结构体Phone,该类结构体需要实现开机功能,实现如下

1
2
3
func (p Phone) start() {
fmt.Println(p.Name, "开机")
}

则可以通过Phone实例直接调用。

1
2
p := Phone{Name: "OPPO"}
p.start()

匿名函数

没有函数名的函数,无法像普通函数那样调用,所以匿名函数需要保存到某个变量(类似于Java中的interface)或者作为立即执行函数(匿名自执行函数)

基本格式

1
2
3
func(参数) (返回结果){
// 函数体
}

匿名自执行函数

1
2
3
4
// 匿名自执行函数
func(入参) {
// 函数体
}(参数)

自定义类型

自定义函数类型与自定义变量类型

1
2
3
4
5
6
7
8
9
10
11
type calc func(int, int) int
type myInt int

func main() {
var c1 calc = getTwoSum
fmt.Printf("自定义函数类型的类型:%T\n", c1) // 自定义的 main.calc 类型
c2 := getTwoSum
fmt.Printf("自定义函数的类型:%T\n", c2) // 推导得出的是function类型 func(int, int) int
var c3 myInt
fmt.Printf("自定义变量类型的类型:%T\n", c3) // main.myInt
}

闭包

可以理解为“定义在一个函数内部的函数”,本质上是将函数内部和函数外部连接起来的桥梁,或者说是函数和其引用环境的组合体。

  • 有权访问另一个函数作用域中变量的函数
  • 创建闭包的常见的方式就是在一个函数内部创建另一个函数,并通过另一个函数的局部变量

为什么需要闭包?全局变量常驻内存但是污染全局,局部变量不污染全局但是不常驻内存,而闭包既可以让一个变量常驻内存,又不污染全局。

需要注意的是:闭包作用域返回的局部变量资源不会被立刻销毁回收,所以可能占用更多的内存,因此过多的使用闭包将会导致性能下降。

闭包的简单写法就是在函数中嵌套一个函数,最后返回里面的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type myFunc func() int

// 闭包
func add() myFunc {
i := 1 // 常驻内存,不污染全局
return func() int {
i++
return i
}
}

func main() {
fn := add() // 此处相当于执行了i:=1,并且i常驻内存,fn被赋值为函数 func() int {i++:return i},每一次调用fn(),都会执行一次i++和return i
fmt.Println(fn()) // 2
fmt.Println(fn()) // 3
fmt.Println(fn()) // 4
}

评论