c++ sleep函数_Go语言进阶之路(三):函数和接口

上一期我们说到了《Go语言进阶之路(二):字符串和指针》,这一期来聊一下Go语言的函数和接口,看看它和Java、Python有什么异同点。

一 函数

Go语言的函数用关键字func来定义,函数可以有返回值也可以没有返回值,有返回值的话,返回值写在函数参数的后面:

// 没有返回值的函数,函数参数是int类型的a和bfunc myFunc(a, b int){  // a和b参数同类型    xxx}

带返回值的函数,还可以给返回值命名:

func change(a, b int) (x, y int) {    x = a + 100    y = b + 100    return   //101, 102    //return x, y  //同上    //return y, x  //102, 101}func main(){    a := 1    b := 2    c, d := change(a, b)    println(c, d)}

可变参数函数

Go语言支持可变参数函数,和Java中一样,都是用三个点...来表示参数个数是可变的,可以看下面的例子。另外,可变参数的类型其实是切片,跟Java一样,可变参数其实就是个语法糖。

func sum(nums ...int) {  fmt.Println(reflect.TypeOf(nums))  // 输出:[]int  fmt.Println(nums, " ")  total := 0  for _, num := range nums {    total += num  }  fmt.Println(total)}func main() {  sum(1, 2)               // [1 2] 3  sum(1, 2, 3)            // [1 2 3] 6  nums := []int{1, 2, 3, 4}  sum(nums...)            // [1 2 3 4] 10}

方法

一般的,我们把普通定义的函数叫做函数,定义给结构体的函数叫做方法。比如:

func func1(a, b int) int {  // 函数    xxx}type Animal struct {  name string  age int}func (animai Animal) func2(a string, b int) (string, int) {    // 方法    xxx}

这里我们就把func1称作函数,func2称作方法。

匿名函数

Go语言支持匿名函数,和JavaScript中一样,我们可以把函数赋值给一个变量,也可以直接创建一个匿名函数立即执行。

我们把上面的可变参数函数例子改一下,把函数赋值给变量func1,然后调用func1函数:

var func1 = func(nums ...int) {  fmt.Println(reflect.TypeOf(nums))  // 输出:[]int  fmt.Println(nums, " ")  total := 0  for _, num := range nums {    total += num  }  fmt.Println(total)}func main() {  func1(1, 2)               // [1 2] 3  func1(1, 2, 3)            // [1 2 3] 6  nums := []int{1, 2, 3, 4}  func1(nums...)            // [1 2 3 4] 10}

我们创建一个匿名函数立即执行:

func(nums ...int) {  fmt.Println(reflect.TypeOf(nums))  // 输出:[]int  fmt.Println(nums, " ")  // 输出:[1 2 3]  total := 0  for _, num := range nums {    total += num  }  fmt.Println(total) // 输出:6}(1, 2, 3)

创建个匿名函数用于goroutine:

var jobs = make(chan int, 5)var done = make(chan bool)var func1 = func() {  for {    j, more := // 如果通道已被关闭,并且数据已全被取走,则more为false    if more {      fmt.Println("received job", j)    } else {      fmt.Println("received all jobs")      done true      return    }  }}func main() {  go func1()  // 创建goroutine立即执行}

后面会有专门的文章详解goroutine和通道。

二 接口

Go语言不是专门为面向对象设计的语言,但是Go语言也能实现面向对象的功能。在Go语言中,接口类型用interface关键字。比较特殊的是,Go语言中任何数据、任何对象都是interface类型的。Go语言中的interface类型就像Java中的Object类一样。

定义接口

type live interface{  // 定义一个接口  eat()  sleep()}

定义一个表示“生活”的接口,有“吃”函数和“睡”函数。

实现接口

我们定义一个“人”类型,来实现“生活”接口:

type person struct {  name string  age int}func (p person) eat() {  fmt.Println("eat something")}func (p *person) sleep() {  fmt.Println("sleep sometimes")}

可以看到,我们的person类型实现了eat函数和sleep函数。实现方法eat时候,我们把p person写在func关键字后面、eat方法前面,这样,我们就把p person作为了方法eat的接收者,换句话说,person类型实现了接口的eat函数。

当一个类型实现了一个接口的所有函数,那我们就叫做这个类型实现了这个接口。这样,我们创建一个person类型的变量,然后调用它的接口方法就可以了。

var p = person{}p.eat()  // 输出:eat somethingp.sleep()  // 输出:sleep sometimes

指针方法和值方法

等等,上面这个例子我们好像看到了点奇怪的东西,eat函数接收者是person类型,sleep函数接收者是指针类型。有什么区别?

从函数调用方式上看,这两者并没有什么区别。我们可以使用person类型和person的指针类型调用任意一个方法:

type person struct {  name string  age int}func (p person) eat() {  fmt.Println("eat something")}func (p *person) sleep() {  fmt.Println("sleep sometimes")}func main() {  var p1 = person{}  p1.eat()  // 输出:eat something  p1.sleep()  // 输出:sleep sometimes  var p2 = &person{}  p2.eat()  // 输出:eat something  p2.sleep()  // 输出:sleep sometimes}

其实,区别在于,用指针类型作为接收者,可以在函数/方法内部修改这个接收者的数据,而用值类型作为接收者,在函数/方法内部不能修改原接收者的数据。看下面的例子就明白了:

type person struct {  name string  age int}func (p person) eat() {  p.name = "eat"  fmt.Println("eat something")}func (p *person) sleep() {  p.age = 28  fmt.Println("sleep sometimes")}func main() {  var p1 = person{}  fmt.Println(p1)  // 输出:{ 0}  p1.eat()  // 输出:eat something  p1.sleep()  // 输出:sleep sometimes  fmt.Println(p1)  // 输出:{ 28}  var p2 = &person{}  fmt.Println(p2)  // 输出:&{ 0}  p2.eat()  // 输出:eat something  p2.sleep()  // 输出:sleep sometimes  fmt.Println(p2)  // 输出:&{ 28}}

可以看到,p2是指针类型,在调用值方法eat时,eat方法内部修改的name属性也没有反映到p2上面。其实,这和我们调用函数时,把切片作为函数参数一样。在调用函数/方法时,接收者对象也会被复制一份,然后再调用函数/方法。对于值类型,直接就复制了一份数据,所以修改不到原来的值类型接收者;对于指针类型,复制的是指针,因此我们可以在函数/方法内部修改到原接收者的数据。

多态

提到接口,当然要提到Java中非常流行的多态。Go语言也可以实现多态,看下面的例子:

type animal interface {  eat()  sleep()}type person struct {  name string  age  int}func (p person) eat() {  fmt.Println("person eat something")}func (p person) sleep() {  fmt.Println("person sleep sometimes")}type cat struct {  name string  age  int}func (p cat) eat() {  fmt.Println("cat eat something")}func (p cat) sleep() {  fmt.Println("cat sleep sometimes")}func main() {  var a animal = person{"person", 28}  a.eat()  // 输出:person eat something  a.sleep()  // 输出:person sleep sometimes  a = cat{"cat", 5}  a.eat()  // 输出:cat eat something  a.sleep()  // 输出:cat sleep sometimes}

person类型和cat类型都实现了animal接口,创建animal类型变量a,把person类型变量和cat类型变量赋值给a,调用方法时,会多态执行到具体实现上。

空接口/任意类型接口

我们知道,interface{}类型就像Java里面的Object类一样,同时Go语言又具有多态的特性,那么我们可以使用空接口变量来存放任意类型数据。比如:

 var a interface{} = 2 fmt.Println(a)  // 输出:2

我们来创建一个可以存放任意类型的map:

func main() { var m = make(map[string]interface{}) m["a"] = 1 m["b"] = "b" m["c"] = []int{1, 2} fmt.Println(m)  // 输出:map[a:1 b:b c:[1 2]]}

下一期我们聊一下Go语言中的Go语言中的标准错误和异常。

喜欢的可以点个关注或者在看支持一下!

推荐阅读

  • Go语言进阶之路(二):字符串和指针


喜欢本文的朋友,欢迎关注“Go语言中文网”:

9c540c4bf0cf55f23c486cf25ea36652.png

Go语言中文网启用微信学习交流群,欢迎加微信:274768166,投稿亦欢迎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值