找回密码
 立即注册
搜索
查看: 354|回复: 0

[go基础] Go函数作为值与类型

  [复制链接]
匿名
匿名  发表于 2023-2-8 16:29 |阅读模式

在 Go 语言中,我们可以把函数作为一种变量,用 type 去定义它,那么这个函数类型就可以作为值传递,甚至可以实现方法,这一特性实在太灵活了,有时候我们甚至可以利用这一特性进行类型转换。

作为值传递的条件是类型具有相同的参数以及相同的返回值。

函数签名

什么是函数签名

函数类型 又叫 函数签名 , 一个函数的类型就是函数定义首行去掉函数名、参数名和{,可以 使用 fmt.Printf 的”%T”格式化参数打印函数的类型。

什么是函数类型相同

两个函数类型相同的条件是:

拥有相同的形参列表和返回值列表(列表元素的次序、个数和类型都相同),形参名可以不同 。

以下 3 个函数的函数类型完全一样。

1
2
3
func add (a , b int) int { return a + b }
func sub (c int, d int) int { return c - d }
func mul (e int, f int) int { return e * f }

举例:

新建test.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main
import "fmt"

func add(a, b int) int     { return a + b }
func sub(c int, d int) int { return c - d }
func mul(e int, f int) int { return e * f }

func main() {
	fmt.Printf("%T\n", add)
	fmt.Printf("%T\n", sub)
	fmt.Printf("%T\n", mul)
}

执行结果:

1
2
3
func(int, int) int
func(int, int) int
func(int, int) int

通过Type定义函数类型

搞明白了什么是函数类型相同之后,接下来了解通过Type定义函数类型。

通过 type 可以定义函数类型,格式如下


type typeName func(arguments) retType

函数类型也是一种类型,故可以将其定义为函数入参,在 go 语言中函数名可以看做是函数类型的常量,所以我们可以直接将函数名作为参数传入的函数中。

例如:

 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
package main

import "fmt"

func add(a, b int) int {
	return a + b
}

//sub作为函数名可以看成是 op 类型的常量
func sub(a, b int) int {
	return a - b
}

// ***************************
// 定义函数类型 op
// ***************************
type op func(a, b int) int

// ***************************
// 形参指定传入参数为函数类型op
// ***************************
func Oper(fu op, a, b int) int {
	return fu(a, b)
}

func main() {
	//在go语言中函数名可以看做是函数类型的常量,所以我们可以直接将函数名作为参数传入的函数中。
	aa := Oper(add, 1, 2)
	fmt.Println(aa)
	bb := Oper(sub, 1, 2)
	fmt.Println(bb)
}

函数的类型转换

Go 语言的类型转换基本格式如下:

1
2
// 类型转换
type_name(expression)

举个例子:

本例声明了一个 CalculateType 函数类型,并实现 Serve() 方法,并将拥有相同参数的 add 和 mul 强制转换成 CalculateType 函数类型,同时这两个函数都拥有了 CalculateType 函数类型的 Serve() 方法。

 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
package main

import "fmt"

// 声明了一个函数类型
type CalculateType func(int, int)

// 该函数类型 CalculateType 实现了一个方法
func (c *CalculateType) Serve() {
	fmt.Println("我是一个函数类型")
}

// 加法函数
func add2(a, b int) {
	fmt.Println(a + b)
}

// 乘法函数
func mul2(a, b int) {
	fmt.Println(a * b)
}

func main() {
	a := CalculateType(add2) // 将 add2 函数强制转换成 CalculateType 类型
	b := CalculateType(mul2) // 将 mul2 函数强制转换成 CalculateType 类型
	a(2, 3)                  // 5
	b(2, 3)                  // 6
	a.Serve()                // 我是一个函数类型
	b.Serve()                // 我是一个函数类型
}

函数作参数传递

如下示例,Calculate 的 f 参数类型为 CalculateType,add 和 mul 函数具有和 CalculateType 函数类型相同的参数和返回值,因此可以将 add 和 mul 函数作为参数传入 Calculate 函数中。

 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
package main

import "fmt"

// 函数作为参数传递

type CalculateType3 func(a, b int) int // 声明了一个函数类型

// 加法函数
func add3(a, b int) int {
	return a + b
}

// 乘法函数
func mul3(a, b int) int {
	return a * b
}

func Calculate3(a, b int, f CalculateType3) int {
	return f(a, b)
}

func main() {
	a, b := 2, 3
	fmt.Println(Calculate3(a, b, add3)) // 5
	fmt.Println(Calculate3(a, b, mul3)) // 6
}

net/http 包源码例子

http.HandleFunc()是一个注册函数,传一个string类型的路由,和一个函数,函数的参数为**(http.ResponseWriter, *http.Request)**。

跟踪进入函数,在 golang 源码 net/http/server.go 文件中

1
2
3
4
5
6
7
// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
  DefaultServeMux.HandleFunc(pattern, handler)
}

在 HandleFunc 中调用了 DefaultServeMux.HandleFunc(pattern, handler)

1
2
3
4
5
6
7
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
  // 将传入的函数 handler 强制转换成 HandlerFunc 
  // 本示例是 sayHi 函数
  mux.Handle(pattern, HandlerFunc(handler))
}

1
2
3
4
5
6
7
8
9
// 定义函数类型
type HandlerFunc func(ResponseWriter, *Request)

// 函数类型的方法
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
  f(w, r)
}

HandlerFunc 实现了 Handler 接口

Handler接口定义:

1
2
3
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

这段源码的目的是为了将我们自定义的 Handler 强制实现 ServeHTTP() 方法,如下例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 自定义函数
// 函数类型是 HandlerFunc
func sayHi(w http.ResponseWriter, r *http.Request) {
  io.WriteString(w, "hi")
}

func main() {
  // (2) 
  http.HandlerFunc("/", sayHi)
  http.ListenAndserve(":8080", nil)
}

因为 HandlerFunc 是一个函数类型,而 sayHi 函数拥有和 HandlerFunc 函数类型一样的参数值,因此可以将 sayHi 强制转换成 HandlerFunc,因此 sayHi 也拥有了 ServeHTTP() 方法,也就实现了 Handler 接口,同时,HandlerFunc 的 ServeHTTP 方法执行了它自己本身,也就是 sayHi 函数,这也就可以看出来了,sayHi 就是 Handler 被调用之后的执行结果。

 

原文地址:http://www.manoner.com/post/GoLand/Go%E5%87%BD%E6%95%B0%E4%BD%9C%E4%B8%BA%E5%80%BC%E4%B8%8E%E7%B1%BB%E5%9E%8B/

 

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|学习笔记

GMT+8, 2024-12-21 22:24 , Processed in 0.020485 second(s), 14 queries , APCu On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表