golang中函数,方法和接口的浅析(函数篇)

golang中函数,方法和接口的浅析(函数篇)

前言

函数对应操作序列,是程序的基本组成元素。Go语言中的函数有具名和匿名之分:具名函数一般对应于包级的函数,是匿名函数的一种特例。当匿名函数引用了外部的变量,那么这个匿名函数就变成了闭包函数,闭包函数是函数编程语言的核心。方法是绑定到一个具体类型的特殊函数,Go语言中的方法是依托于类型的,必须在编译时静态绑定。接口定义了方法的集合,这些方法依托于运行时的接口对象,因此接口对应的方法是在运行时动态绑定的。Go语言通过隐式接口机制实现了鸭子模型(duck typing)

函数

在golang中函数时第一类对象,可以将函数保存到变量中。函数主要有具名和匿名之分,包级函数一般都是具名函数,具名函数时匿名函数的一种特例。当然Go语言中每个类型还可以有自己的方法,方法其实也时函数的一种

//具名函数
func Add(a,b,int)int{
	return a+b
}
//匿名函数
var Add = func(a,b int) int{
	return a+b
}

Go语言中的函数可以有多个参数和多个返回值,参数和返回值都是以传值的反水和被调用者交换数据。在语法上,函数还支持可变数量的参数,可变数量的参数必须是最后出现的参数,可变数量的参数其实是一个切片类型的参数。

//多个参数和多个返回值
func Swap(a,b int) (int,int){
	return b,a
}

//可变数量的参数
//more对应[]int切片类型
func Sum(a int,more ...int) int{
	for _, v:= range more{
		a +=v
	}
	retrun a
}

当可变参数是一个空接口时,调用者是否解包可变参数会导致不同的结果:

func maimn(){
	var a = []interface{}{123."abc"}
	Print(a...)//123 abc
	Print(a)//[123 abc]
}

Print(a ....interface{}){

	fmt.Println(a...)
}

第一个Print调用时传入的参数时a…,等价雨直接调用Print(123,“abc”).第二个Println调用传入的时未解包的a,等价于直接调用Print([]interface{}{123,“abc”})。不仅函数的参数可以有名字,可以给函数的返回值命名:

func Find(m map[int]int,key int)(value int,ok bool){
		value,ok = m[key]
		return
}

如果返回值命名了,可以通过名字来修改返回值,也可以通过defer语句在return之后修改返回值

func Inc(v int){
	defer func(){v++}()
	return 42
}

其中defer语句延迟执行了一个匿名函数,应为这个函数不捕获了外部函数的局部变量v,这种函数我们一般称为闭包。闭包对捕获的外部变量并不是以传值方式访问,而实以引用方式访问。

闭包的这种以引用方式访问外部变量的行为可能会导致一些隐含的问题;

func main(){
	for i:= 0;i<3;i++{
		defer func() {println(i)}()
	}
}

输出:

output

3

3

3

因为是闭包,在for迭代语句中,每一个defer语句延迟执行的函数引用的都是同一个i迭代变量,在循环结束后这个变量的值为3,因此最终输出的都是3.

修复的思路是在每轮迭代中为每个defer语句的闭包函数生成独有的变量。可以用下面两种方式:

func mian(){
	for i :=0;i<3;i++{
		i :=i//定义一个循环体内局部变量i、
		defer func(){println(i)}()
	}
}
-------------------------------------------------------
func main(){
		for i :=0;i<3;i++{
		//通过函数传入i
		//defer语句会马上对调用参数求值
		defer func(i int){println(i)}(i)
	}
}

第一种方法是在循环体内部再定义一个局部变量,这样每次迭代defer语句的闭包函数捕获的都是不同的变量,这些变量的值对应迭代时的值。第二种方式是将迭代变量通过闭包函数的参数传入,defer会马上对调用参数求值。两种方式都是可以工作的。不过一般来说,在for循环内部执行defer语句斌不是一个好的习惯。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Go语言接口是一种定义行为的类型。通过实现接口,可以使不同的类型具有相同的方法集合,从而实现多态性。下面是在Go语言实现接口函数的步骤: 1. 定义接口:首先需要定义一个接口接口由一组方法组成。方法可以是任何类型的函数,只要它们具有相同的名称和签名。 2. 实现接口:接下来,需要在一个类型上实现接口定义的方法。要实现一个接口,只需在类型上定义与接口方法签名相匹配的方法即可。 3. 使用接口:一旦一个类型实现了接口,就可以将该类型的实例赋值给接口类型的变量。通过接口变量,可以调用实现了接口方法的类型的方法。 下面是一个简单的示例,演示了如何在Go语言实现接口函数: ```go // Step 1: 定义接口 type Shape interface { Area() float64 } // Step 2: 实现接口 type Rectangle struct { Width float64 Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height } type Circle struct { Radius float64 } func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius } // Step 3: 使用接口 func PrintArea(s Shape) { fmt.Println("Area:", s.Area()) } func main() { rect := Rectangle{Width: 3, Height: 4} circle := Circle{Radius: 5} PrintArea(rect) // 输出:Area: 12 PrintArea(circle) // 输出:Area: 78.53981633974483 } ``` 在上面的示例,我们定义了一个`Shape`接口,它包含一个`Area`方法。然后,我们分别在`Rectangle`和`Circle`类型上实现了`Area`方法。最后,在`main`函数,我们创建了一个`Rectangle`类型的实例和一个`Circle`类型的实例,并将它们传递给`PrintArea`函数来打印它们的面积。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值