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對象實現。

相關文章
相關標籤/搜索