go语言-函数、方法和接口

    Go语言中的函数有具名和匿名之分:具名函数通常对应于包级函数,是匿名函数的一种特例。当匿名函数引用了外部做用于中的变量时就成了闭包函数。方法是绑定到一个具体类型的特殊函数,Go语言中的方法依托于类型的,必须在编译时静态绑定。接口定义了方法的集合,这些方法依托于运行时的接口对象,所以接口对应的方法是在运行时动态绑定的。Go语言经过隐式接口机制实现了鸭子面向对象模型。golang

    1.函数:
闭包

        函数定义:函数大致由函数名,参数及返回值构成。
ide

//具名函数
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
    }
    return a
}

当可变参数是一个空接口类型是,调用者是否解包可变参数会致使不一样的结果:ui

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

func Print(a ...interface{}) {
   fmt.Println(a...)
}

第一个Print调用时传入的是a...,等价于直接调用Print(123, "abc")。第二个Print调用传入的是未解包的a,等价于直接调用Print([]interface{}{123, "abc"})。
this

不只函数的参数能够有名字,也能够给函数的返回值命名。google

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

若是返回值命名了,能够经过名字来修改返回值,也能够经过defer语句在return语句以后修改返回值:插件

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

其中defer语句延迟执行了一个匿名函数,由于这个匿名函数捕获了外部函数的局部变量v,这种函数咱们通常称为“闭包”。闭包对捕获外部变量并非以传值的方式访问,而是以引用的方式访问。
指针

    闭包的这种以饮用方式访问外部变量的行为可能会致使一些隐含的问题。
code

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

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

    修复的思路是在每轮迭代中为每一个defer语句的闭包函数生成独有的变量。

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

//或者

func main() {
   for i:=0;i<3;i++{
      //经过函数传入i
      //defer 语句会立刻对调用参数求值
      defer func(i int) { println(i) } (i)
   }
}

此处仅为示例,不建议在for循环中使用defer


2.接口

Go的接口类型是对其余类型行为的抽象和归纳,由于接口类型不会和特定的实现细节绑定在一块儿,经过这种抽象的方式咱们可让对象更加灵活,更有适应能力。接口类型是延迟绑定,能够实现相似虚函数的多态功能。

Go语言中,基础类型(非接口类型)不支持隐式转换,咱们没法将一个int类型的值直接赋值给int64类型的变量。可是Go语言对于接口类型的转换则很是灵活。对象和接口之间的转换、接口和接口的转换均可能是隐式的转换。

var (
    a io.ReadCloser = (*os.File)(f)  //隐式转换,*os.File知足io.ReadCloser接口
    b io.Reader = a                  //隐式转换,io.ReaderCloser知足io.Reader接口
    c io.Closer = a                  //隐式转换,io.ReaderCloser知足io.Closer接口
    d io.Reader = c.(io.Reader)      //显式转换,io.Cloder不知足io.Reader接口
)

咱们能够经过嵌入匿名接口或嵌入匿名指针对象来实现纯虚集成,继承的只是接口指定的规范,真正的实如今运行的时候才被注入。例如,能够实现一个gRPC的插件:

type grpcPlugin struct {
    *generator.Generator
}

func (p *grpcPlugin) Name() string { return "grpc" }

func (p *grpcPlugin) Init(g *generator.Generator) {
    p.Generator = g
}

func (p *grpcPlugin) GenerateImports(file *generator.FileDescription) {
    if len(file.Service) == 0 {
        return
    }
    p.P('import "google.golang.org/grpc"')
}

构造的grpcPlugin类型对象必须知足generate.Plugin接口:

type Plugin interface {
    //Name identifies the plugin.
    Name() string
    //Init is called once after data structures are built but before
    //code generation begins
    Init(g *Generator)
    //Generate produce the code generated by the plugin for this file,
    //except for the imports, by calling the generator's methods
    //P, In ,and Out.
    Generate(file *FileDescriptor)
    //GenerateImports produces the import declarations for this file.
    //It is called after Generate.
    GenerateImports(file *FileDescripor)
}

generate.Plugin 接口对应的grpcPlugin类型的GenerateImports()方法中使用的p.P(...)函数,倒是经过Init()函数注入的generator.Generator对象实现。

相关文章
相关标签/搜索