关于golang:go语言学习函数方法与接口

51次阅读

共计 4218 个字符,预计需要花费 11 分钟才能阅读完成。

概述

function,函数

function,函数,是一个能够被其余代码或其本身调用的 代码片段。当函数被调用时,参数被作为输出传递给函数,并且函数能够返回输入。

函数能够让咱们将一个语句序列打包为一个单元,而后能够从程序中其它中央屡次调用。函数的机制能够让咱们将一个大的工作合成为小的工作,这样的小工作能够让不同程序员在不同工夫、不同中央独立实现。一个函数同时对用户暗藏了其实现细节。因为这些因素,对于任何编程语言来说,函数都是一个至关重要的局部。

在不同的编程语言中,函数的类型有不同的定义。例如,在 JavaScript 中,函数也是一个对象。而在 golang 之中,函数是一个被独自定义的类型。函数被看作第一类值(first-class values):函数像其余值一样,领有类型,能够被赋值给其余变量,传递给函数,从函数返回。

第一类值的个性如下

能够被存入变量或其余构造
能够被作为参数传递给其余函数
能够被作为函数的返回值
能够在执行期发明,而无需齐全在设计期全副写出
即便没有被系结至某一名称,也能够存在

与之相比拟的是,例如在 C 语言与 C ++ 中的函数不是第一类对象,因为在这些语言中函数不能在执行期发明,而必须在设计时全副写好。

函数能够分为具名函数和匿名函数,具名函数个别对应于包级的函数,是匿名函数的一种特例,当匿名函数援用了内部作用域中的变量时就成了闭包函数,闭包函数是函数式编程语言的外围。

办法,method

在 Go 语言中,构造体就像是类的一种简化模式,那么类的办法在哪里呢?在 Go 语言中有一个概念,它和办法有着同样的名字,并且大体上意思雷同,go 中的办法个别指的是静态方法,就是一个蕴含了接受者(receiver)的函数,receiver 能够是内置类型或者构造体类型的一个值或者是一个指针。

接收器类型能够是(简直)任何类型,不仅仅是构造体类型,任何类型都能够有办法,甚至能够是函数类型,能够是 int、bool、string 或数组的别名类型,然而接收器不能是一个接口类型,因为接口是一个形象定义,而办法却是具体实现,如果这样做了就会引发一个编译谬误

从 90 年代晚期开始,面向对象编程(OOP)就成为了称霸工程界和教育界的编程范式,所以之后简直所有大规模被利用的语言都蕴含了对 OOP 的反对,go 语言也不例外。
援用
只管没有被公众所承受的明确的 OOP 的定义,从咱们的了解来讲,一个对象其实也就是一个简略的值或者一个变量,在这个对象中会蕴含一些办法,而一个办法则是一个一个和非凡类型关联的函数。一个面向对象的程序会用办法来表白其属性和对应的操作,这样应用这个对象的用户就不须要间接去操作对象,而是借助办法来做这些事件。

办法是绑定到一个具体类型的非凡函数,Go 语言中的办法是依靠于类型的,必须在编译时动态绑定。

接口

Go 语言提供了另外一种数据类型即接口,它把所有的具备共性的办法定义在一起,任何其余类型只有实现了这些办法就是实现了这个接口。

接口定义了办法的汇合,这些办法依靠于运行时的接口对象,因而接口对应的办法是在运行时动静绑定的。Go 语言通过隐式接口机制实现了鸭子面向对象模型。

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就能够被称为鸭子。”

go 鸭子类型的疑难?– DJun 的答复 – 知乎
https://www.zhihu.com/questio…

go 程序的执行程序

Go 语言程序的初始化和执行总是从 main.main 函数开始的。然而如果 main 包导入了其它的包,则会依照程序将它们蕴含进 main 包里(这里的导入程序依赖具体实现,个别可能是以文件名或包路径名的字符串程序导入)。如果某个包被屡次导入的话,在执行的时候只会导入一次。当一个包被导入时,如果它还导入了其它的包,则先将其它的包蕴含进来,而后创立和初始化这个包的常量和变量, 再调用包里的 init 函数,如果一个包有多个 init 函数的话,调用程序未定义(实现可能是以文件名的顺序调用),同一个文件内的多个 init 则是以呈现的程序顺次调用(init 不是一般函数,能够定义有多个,所以也不能被其它函数调用)。最初,当 main 包的所有包级常量、变量被创立和初始化实现,并且 init 函数被执行后,才会进入 main.main 函数,程序开始失常执行。下图是 Go 程序函数启动程序的示意图:

要留神的是,在 main.main 函数执行之前所有代码都运行在同一个 goroutine,也就是程序的主零碎线程中。因而,如果某个 init 函数外部用 go 关键字启动了新的 goroutine 的话,新的 goroutine 只有在进入 main.main 函数之后才可能被执行到。

函数

申明

func name(parameter-list) (result-list) {body}

参数

在函数体中,函数的形参作为局部变量,被初始化为调用者提供的值。函数的形参和有名返回值作为函数最外层的局部变量,被存储在雷同的词法块中。

实参通过值的形式传递,因而函数的形参是实参的拷贝。对形参进行批改不会影响实参。然而,如果实参包含援用类型,如指针,slice(切片)、map、function、channel 等类型,实参可能会因为函数的间接援用被批改。

变长参数和变长参数函数类型

一个函数的最初一个参数能够是一个变长参数。一个函数能够最多有一个变长参数。一个变长参数的类型总为一个切片类型。变长参数在申明的时候必须在它的(切片)类型的元素类型后面前置三个点 …,以示这是一个变长参数。

变长函数申明和一般函数申明相似,只不过最初一个参数必须为变长参数。一个变长参数在函数体内将被视为一个切片。

// Sum 返回所有输出实参的和。func Sum(values ...int64) (sum int64) {// values 的类型为[]int64。sum = 0
    for _, v := range values {sum += v}
    return
}

// Concat 是一个低效的字符串拼接函数。func Concat(sep string, tokens ...string) string {// tokens 的类型为[]string。r := ""
    for i, t := range tokens {
        if i != 0 {r += sep}
        r += t
    }
    return r
}

函数返回多个值

Go 函数能够返回多个值,例如:

package main

import "fmt"

func swap(x, y string) (string, string) {return y, x}

func main() {a, b := swap("Google", "Runoob")
   fmt.Println(a, b)
}

递归调用

Go 语言中,函数还能够间接或间接地调用本人,也就是反对递归调用。Go 语言函数的递归调用深度逻辑上没有限度,函数调用的栈是不会呈现溢出谬误的,因为 Go 语言运行时会依据须要动静地调整函数栈的大小。每个 goroutine 刚启动时只会调配很小的栈(4 或 8KB,具体依赖实现),依据须要动静调整栈的大小,栈最大能够达到 GB 级(依赖具体实现,在目前的实现中,32 位体系结构为 250MB,64 位体系结构为 1GB)。在 Go1.4 以前,Go 的动静栈采纳的是分段式的动静栈,艰深地说就是采纳一个链表来实现动静栈,每个链表的节点内存地位不会发生变化。然而链表实现的动静栈对某些导致逾越链表不同节点的热点调用的性能影响较大,因为相邻的链表节点它们在内存地位个别不是相邻的,这会减少 CPU 高速缓存命中失败的几率。为了解决热点调用的 CPU 缓存命中率问题,Go1.4 之后改用间断的动静栈实现,也就是采纳一个相似动静数组的构造来示意栈。不过间断动静栈也带来了新的问题:当间断栈动静增长时,须要将之前的数据挪动到新的内存空间,这会导致之前栈中全副变量的地址发生变化。尽管 Go 语言运行时会自动更新援用了地址变动的栈变量的指针,但最重要的一点是要明确 Go 语言中指针不再是固定不变的了(因而不能随便将指针放弃到数值变量中,Go 语言的地址也不能随便保留到不在 GC 管制的环境中,因而应用 CGO 时不能在 C 语言中长期持有 Go 语言对象的地址)

办法

语法

method 的语法如下:

func (r ReceiverType) funcName(parameters) (results)

当调用 method 时,会将 receiver 作为函数的第一个参数:

funcName(r, parameters)

利用组合的语法实现办法的继承

例如:

type Base struct {name string}

func (base *Base) Set(myname string) {base.name = myname}

func (base *Base) Get() string {return base.name}

type Derived struct {
    Base
    age int 
}

func (derived *Derived) Get() (nm string, ag int) {return derived.name, derived.age}


func main() {b := &Derived{}

    b.Set("sina")
    fmt.Println(b.Get())
}

例子中,在 Base 类型定义了 get()和 set()两个办法,而 Derived 类型继承了 Base 类,并改写了 Get()办法,在 Derived 对象调用 Set()办法,会加载基类对应的办法;而调用 Get()办法时,加载派生类改写的办法。

接口

Interface 是一组形象办法(未具体实现的办法 / 仅蕴含办法名参数返回值的办法)的汇合,如果实现了 interface 中的所有办法,即该类 / 对象就实现了该接口。

Interface 的申明格局:

type interfaceName interface {// 办法列表}  

Interface 能够被任意对象实现,一个类型 / 对象也能够实现多个 interface;
interface 的变量能够持有任意实现该 interface 类型的对象。

参考

https://books.studygolang.com…
https://books.studygolang.com…
https://developer.mozilla.org…
https://baike.baidu.com/item/…
https://www.cnblogs.com/chenn…
http://c.biancheng.net/view/6…

正文完
 0