在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ GO/ 2.6 interface
7 文本處理
3 Web基礎(chǔ)
14 擴展Web框架
10.4 小結(jié)
2.2 Go基礎(chǔ)
2.8 總結(jié)
6.1 session和cookie
5.5 使用beedb庫進行ORM開發(fā)
8.3 REST
13.6 小結(jié)
5.4 使用PostgreSQL數(shù)據(jù)庫
14.6 pprof支持
14.1 靜態(tài)文件支持
11.2 使用GDB調(diào)試
7.7 小結(jié)
1 GO環(huán)境配置
14.5 多語言支持
7.1 XML處理
1.5 總結(jié)
13 如何設(shè)計一個Web框架
14.3 表單及驗證支持
12 部署與維護
10 國際化和本地化
1.1 Go 安裝
6.2 Go如何使用session
5.6 NOSQL數(shù)據(jù)庫操作
6.5 小結(jié)
9.4 避免SQL注入
12.1 應(yīng)用日志
4.2 驗證表單的輸入
10.1 設(shè)置默認地區(qū)
1.3 Go 命令
9.6 加密和解密數(shù)據(jù)
4.1 處理表單的輸入
4.4 防止多次遞交表單
11.3 Go怎么寫測試用例
8 Web服務(wù)
12.3 應(yīng)用部署
5.7 小結(jié)
12.5 小結(jié)
11 錯誤處理,調(diào)試和測試
9.2 確保輸入過濾
14.2 Session支持
6.4 預(yù)防session劫持
12.4 備份和恢復(fù)
8.1 Socket編程
13.1 項目規(guī)劃
13.4 日志和配置設(shè)計
7.6 字符串處理
13.2 自定義路由器設(shè)計
6.3 session存儲
3.4 Go的http包詳解
8.2 WebSocket
10.3 國際化站點
7.5 文件操作
7.4 模板處理
9.1 預(yù)防CSRF攻擊
13.3 controller設(shè)計
2.6 interface
14.4 用戶認證
2.3 流程和函數(shù)
附錄A 參考資料
11.1 錯誤處理
9.5 存儲密碼
9.3 避免XSS攻擊
12.2 網(wǎng)站錯誤處理
6 session和數(shù)據(jù)存儲
2.4 struct類型
3.3 Go如何使得Web工作
2.5 面向?qū)ο?/span>
3.1 Web工作方式
1.2 GOPATH與工作空間
2.1 你好,Go
9.7 小結(jié)
13.5 實現(xiàn)博客的增刪改
7.2 JSON處理
10.2 本地化資源
7.3 正則處理
2 Go語言基礎(chǔ)
5.1 database/sql接口
4.5 處理文件上傳
8.5 小結(jié)
4.3 預(yù)防跨站腳本
5.3 使用SQLite數(shù)據(jù)庫
14.7 小結(jié)
3.2 Go搭建一個Web服務(wù)器
2.7 并發(fā)
5 訪問數(shù)據(jù)庫
4 表單
3.5 小結(jié)
1.4 Go開發(fā)工具
11.4 小結(jié)
9 安全與加密
5.2 使用MySQL數(shù)據(jù)庫
4.6 小結(jié)
8.4 RPC

2.6 interface

interface

Go語言里面設(shè)計最精妙的應(yīng)該算interface,它讓面向?qū)ο螅瑑?nèi)容組織實現(xiàn)非常的方便,當你看完這一章,你就會被interface的巧妙設(shè)計所折服。

什么是interface

簡單的說,interface是一組method的組合,我們通過interface來定義對象的一組行為。

我們前面一章最后一個例子中Student和Employee都能SayHi,雖然他們的內(nèi)部實現(xiàn)不一樣,但是那不重要,重要的是他們都能say hi

讓我們來繼續(xù)做更多的擴展,Student和Employee實現(xiàn)另一個方法Sing,然后Student實現(xiàn)方法BorrowMoney而Employee實現(xiàn)SpendSalary。

這樣Student實現(xiàn)了三個方法:SayHi、Sing、BorrowMoney;而Employee實現(xiàn)了SayHi、Sing、SpendSalary。

上面這些方法的組合稱為interface(被對象Student和Employee實現(xiàn))。例如Student和Employee都實現(xiàn)了interface:SayHi和Sing,也就是這兩個對象是該interface類型。而Employee沒有實現(xiàn)這個interface:SayHi、Sing和BorrowMoney,因為Employee沒有實現(xiàn)BorrowMoney這個方法。

interface類型

interface類型定義了一組方法,如果某個對象實現(xiàn)了某個接口的所有方法,則此對象就實現(xiàn)了此接口。詳細的語法參考下面這個例子

type Human struct {
    name string
    age int
    phone string
}

type Student struct {
    Human //匿名字段Human
    school string
    loan float32
}

type Employee struct {
    Human //匿名字段Human
    company string
    money float32
}

//Human對象實現(xiàn)Sayhi方法
func (h *Human) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

// Human對象實現(xiàn)Sing方法
func (h *Human) Sing(lyrics string) {
    fmt.Println("La la, la la la, la la la la la...", lyrics)
}

//Human對象實現(xiàn)Guzzle方法
func (h *Human) Guzzle(beerStein string) {
    fmt.Println("Guzzle Guzzle Guzzle...", beerStein)
}

// Employee重載Human的Sayhi方法
func (e *Employee) SayHi() {
    fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
        e.company, e.phone) //此句可以分成多行
}

//Student實現(xiàn)BorrowMoney方法
func (s *Student) BorrowMoney(amount float32) {
    s.loan += amount // (again and again and...)
}

//Employee實現(xiàn)SpendSalary方法
func (e *Employee) SpendSalary(amount float32) {
    e.money -= amount // More vodka please!!! Get me through the day!
}

// 定義interface
type Men interface {
    SayHi()
    Sing(lyrics string)
    Guzzle(beerStein string)
}

type YoungChap interface {
    SayHi()
    Sing(song string)
    BorrowMoney(amount float32)
}

type ElderlyGent interface {
    SayHi()
    Sing(song string)
    SpendSalary(amount float32)
}

通過上面的代碼我們可以知道,interface可以被任意的對象實現(xiàn)。我們看到上面的Men interface被Human、Student和Employee實現(xiàn)。同理,一個對象可以實現(xiàn)任意多個interface,例如上面的Student實現(xiàn)了Men和YoungChap兩個interface。

最后,任意的類型都實現(xiàn)了空interface(我們這樣定義:interface{}),也就是包含0個method的interface。

interface值

那么interface里面到底能存什么值呢?如果我們定義了一個interface的變量,那么這個變量里面可以存實現(xiàn)這個interface的任意類型的對象。例如上面例子中,我們定義了一個Men interface類型的變量m,那么m里面可以存Human、Student或者Employee值。

因為m能夠持有這三種類型的對象,所以我們可以定義一個包含Men類型元素的slice,這個slice可以被賦予實現(xiàn)了Men接口的任意結(jié)構(gòu)的對象,這個和我們傳統(tǒng)意義上面的slice有所不同。

讓我們來看一下下面這個例子:

package main
import "fmt"

type Human struct {
    name string
    age int
    phone string
}

type Student struct {
    Human //匿名字段
    school string
    loan float32
}

type Employee struct {
    Human //匿名字段
    company string
    money float32
}

//Human實現(xiàn)SayHi方法
func (h Human) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

//Human實現(xiàn)Sing方法
func (h Human) Sing(lyrics string) {
    fmt.Println("La la la la...", lyrics)
}

//Employee重載Human的SayHi方法
func (e Employee) SayHi() {
    fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
        e.company, e.phone)
    }

// Interface Men被Human,Student和Employee實現(xiàn)
// 因為這三個類型都實現(xiàn)了這兩個方法
type Men interface {
    SayHi()
    Sing(lyrics string)
}

func main() {
    mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
    paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
    sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
    tom := Employee{Human{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000}

    //定義Men類型的變量i
    var i Men

    //i能存儲Student
    i = mike
    fmt.Println("This is Mike, a Student:")
    i.SayHi()
    i.Sing("November rain")

    //i也能存儲Employee
    i = tom
    fmt.Println("This is tom, an Employee:")
    i.SayHi()
    i.Sing("Born to be wild")

    //定義了slice Men
    fmt.Println("Let's use a slice of Men and see what happens")
    x := make([]Men, 3)
    //這三個都是不同類型的元素,但是他們實現(xiàn)了interface同一個接口
    x[0], x[1], x[2] = paul, sam, mike

    for _, value := range x{
        value.SayHi()
    }
}

通過上面的代碼,你會發(fā)現(xiàn)interface就是一組抽象方法的集合,它必須由其他非interface類型實現(xiàn),而不能自我實現(xiàn), Go通過interface實現(xiàn)了duck-typing:即"當看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那么這只鳥就可以被稱為鴨子"。

空interface

空interface(interface{})不包含任何的method,正因為如此,所有的類型都實現(xiàn)了空interface。空interface對于描述起不到任何的作用(因為它不包含任何的method),但是空interface在我們需要存儲任意類型的數(shù)值的時候相當有用,因為它可以存儲任意類型的數(shù)值。它有點類似于C語言的void*類型。

// 定義a為空接口
var a interface{}
var i int = 5
s := "Hello world"
// a可以存儲任意類型的數(shù)值
a = i
a = s

一個函數(shù)把interface{}作為參數(shù),那么他可以接受任意類型的值作為參數(shù),如果一個函數(shù)返回interface{},那么也就可以返回任意類型的值。是不是很有用啊!

interface函數(shù)參數(shù)

interface的變量可以持有任意實現(xiàn)該interface類型的對象,這給我們編寫函數(shù)(包括method)提供了一些額外的思考,我們是不是可以通過定義interface參數(shù),讓函數(shù)接受各種類型的參數(shù)。

舉個例子:fmt.Println是我們常用的一個函數(shù),但是你是否注意到它可以接受任意類型的數(shù)據(jù)。打開fmt的源碼文件,你會看到這樣一個定義:

type Stringer interface {
     String() string
}

也就是說,任何實現(xiàn)了String方法的類型都能作為參數(shù)被fmt.Println調(diào)用,讓我們來試一試

package main
import (
    "fmt"
    "strconv"
)

type Human struct {
    name string
    age int
    phone string
}

// 通過這個方法 Human 實現(xiàn)了 fmt.Stringer
func (h Human) String() string {
    return "?"+h.name+" - "+strconv.Itoa(h.age)+" years -  ? " +h.phone+"?"
}

func main() {
    Bob := Human{"Bob", 39, "000-7777-XXX"}
    fmt.Println("This Human is : ", Bob)
}

現(xiàn)在我們再回顧一下前面的Box示例,你會發(fā)現(xiàn)Color結(jié)構(gòu)也定義了一個method:String。其實這也是實現(xiàn)了fmt.Stringer這個interface,即如果需要某個類型能被fmt包以特殊的格式輸出,你就必須實現(xiàn)Stringer這個接口。如果沒有實現(xiàn)這個接口,fmt將以默認的方式輸出。

//實現(xiàn)同樣的功能
fmt.Println("The biggest one is", boxes.BiggestsColor().String())
fmt.Println("The biggest one is", boxes.BiggestsColor())

注:實現(xiàn)了error接口的對象(即實現(xiàn)了Error() string的對象),使用fmt輸出時,會調(diào)用Error()方法,因此不必再定義String()方法了。

interface變量存儲的類型

我們知道interface的變量里面可以存儲任意類型的數(shù)值(該類型實現(xiàn)了interface)。那么我們怎么反向知道這個變量里面實際保存了的是哪個類型的對象呢?目前常用的有兩種方法:

  • Comma-ok斷言

    Go語言里面有一個語法,可以直接判斷是否是該類型的變量: value, ok = element.(T),這里value就是變量的值,ok是一個bool類型,element是interface變量,T是斷言的類型。

    如果element里面確實存儲了T類型的數(shù)值,那么ok返回true,否則返回false。

    讓我們通過一個例子來更加深入的理解。

      package main
    
      import (
          "fmt"
          "strconv"
      )
    
      type Element interface{}
      type List [] Element
    
      type Person struct {
          name string
          age int
      }
    
      //定義了String方法,實現(xiàn)了fmt.Stringer
      func (p Person) String() string {
          return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"
      }
    
      func main() {
          list := make(List, 3)
          list[0] = 1 // an int
          list[1] = "Hello" // a string
          list[2] = Person{"Dennis", 70}
    
          for index, element := range list {
              if value, ok := element.(int); ok {
                  fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
              } else if value, ok := element.(string); ok {
                  fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
              } else if value, ok := element.(Person); ok {
                  fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
              } else {
                  fmt.Printf("list[%d] is of a different type\n", index)
              }
          }
      }

    是不是很簡單啊,同時你是否注意到了多個if里面,還記得我前面介紹流程時講過,if里面允許初始化變量。

    也許你注意到了,我們斷言的類型越多,那么if else也就越多,所以才引出了下面要介紹的switch。

  • switch測試

    最好的講解就是代碼例子,現(xiàn)在讓我們重寫上面的這個實現(xiàn)

      package main
    
      import (
          "fmt"
          "strconv"
      )
    
      type Element interface{}
      type List [] Element
    
      type Person struct {
          name string
          age int
      }
    
      //打印
      func (p Person) String() string {
          return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"
      }
    
      func main() {
          list := make(List, 3)
          list[0] = 1 //an int
          list[1] = "Hello" //a string
          list[2] = Person{"Dennis", 70}
    
          for index, element := range list{
              switch value := element.(type) {
                  case int:
                      fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
                  case string:
                      fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
                  case Person:
                      fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
                  default:
                      fmt.Println("list[%d] is of a different type", index)
              }
          }
      }

    這里有一點需要強調(diào)的是:element.(type)語法不能在switch外的任何邏輯里面使用,如果你要在switch外面判斷一個類型就使用comma-ok。

嵌入interface

Go里面真正吸引人的是它內(nèi)置的邏輯語法,就像我們在學(xué)習(xí)Struct時學(xué)習(xí)的匿名字段,多么的優(yōu)雅啊,那么相同的邏輯引入到interface里面,那不是更加完美了。如果一個interface1作為interface2的一個嵌入字段,那么interface2隱式的包含了interface1里面的method。

我們可以看到源碼包container/heap里面有這樣的一個定義

type Interface interface {
    sort.Interface //嵌入字段sort.Interface
    Push(x interface{}) //a Push method to push elements into the heap
    Pop() interface{} //a Pop elements that pops elements from the heap
}

我們看到sort.Interface其實就是嵌入字段,把sort.Interface的所有method給隱式的包含進來了。也就是下面三個方法:

type Interface interface {
    // Len is the number of elements in the collection.
    Len() int
    // Less returns whether the element with index i should sort
    // before the element with index j.
    Less(i, j int) bool
    // Swap swaps the elements with indexes i and j.
    Swap(i, j int)
}

另一個例子就是io包下面的 io.ReadWriter ,它包含了io包下面的Reader和Writer兩個interface:

// io.ReadWriter
type ReadWriter interface {
    Reader
    Writer
}

反射

Go語言實現(xiàn)了反射,所謂反射就是能檢查程序在運行時的狀態(tài)。我們一般用到的包是reflect包。如何運用reflect包,官方的這篇文章詳細的講解了reflect包的實現(xiàn)原理,laws of reflection

使用reflect一般分成三步,下面簡要的講解一下:要去反射是一個類型的值(這些值都實現(xiàn)了空interface),首先需要把它轉(zhuǎn)化成reflect對象(reflect.Type或者reflect.Value,根據(jù)不同的情況調(diào)用不同的函數(shù))。這兩種獲取方式如下:

t := reflect.TypeOf(i)    //得到類型的元數(shù)據(jù),通過t我們能獲取類型定義里面的所有元素
v := reflect.ValueOf(i)   //得到實際的值,通過v我們獲取存儲在里面的值,還可以去改變值

轉(zhuǎn)化為reflect對象之后我們就可以進行一些操作了,也就是將reflect對象轉(zhuǎn)化成相應(yīng)的值,例如

tag := t.Elem().Field(0).Tag  //獲取定義在struct里面的標簽
name := v.Elem().Field(0).String()  //獲取存儲在第一個字段里面的值

獲取反射值能返回相應(yīng)的類型和數(shù)值

var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())

最后,反射的話,那么反射的字段必須是可修改的,我們前面學(xué)習(xí)過傳值和傳引用,這個里面也是一樣的道理。反射的字段必須是可讀寫的意思是,如果下面這樣寫,那么會發(fā)生錯誤

var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1)

如果要修改相應(yīng)的值,必須這樣寫

var x float64 = 3.4
p := reflect.ValueOf(&x)
v := p.Elem()
v.SetFloat(7.1)

上面只是對反射的簡單介紹,更深入的理解還需要自己在編程中不斷的實踐。