Go基礎學習五之接口interface、反射reflection

Go編程語言:支持併發、垃圾回收的編譯型系統級編程語言!本文主要是按照無聞的《Go 編程基礎》開源視頻學習並記錄筆記。編程

1、接口interface

一、基本概念

Go 語言提供了另一種數據類型即接口,它把全部的具備共性的方法定義在一塊兒,任何其餘類型只要實現了這些方法就是實現了這個接口併發

二、定義

/* 定義接口 */
type interface_name interface {
   method_name1 [return_type]
   method_name2 [return_type]
   method_name3 [return_type]
   ...
   method_namen [return_type]
}

/* 定義結構體 */
type struct_name struct {
   /* variables */
}

/* 實現接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
   /* 方法實現 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
   /* 方法實現*/
}

示例:編程語言

package main

import (
    "fmt"
)

type Phone interface {
    call()
}

type NokiaPhone struct {
}

func (nokiaPhone NokiaPhone) call() {
    fmt.Println("I am Nokia, I can call you!")
}

type IPhone struct {
}

func (iPhone IPhone) call() {
    fmt.Println("I am iPhone, I can call you!")
}

func main() {
    var phone Phone

    phone = new(NokiaPhone)
    phone.call()

    phone = new(IPhone)
    phone.call()

}

在上面的例子中,咱們定義了一個接口Phone,接口裏面有一個方法call()。而後咱們在main函數裏面定義了一個Phone類型變量,並分別爲之賦值爲NokiaPhone和IPhone。而後調用call()方法,輸出結果以下:函數

I am Nokia, I can call you!
I am iPhone, I can call you!

三、特性

  • 接口是一個或多個方法簽名的集合
  • 只要某個類型擁有該接口的全部方法簽名,即算實現該接口,無需顯示聲明瞭哪一個接口,這稱爲 Structural Typing
  • 接口只有方法聲明,沒有實現,沒有數據字段
  • 接口能夠匿名嵌入其餘接口,或嵌入到結構中
  • 將對象賦值給接口時,會發生拷貝,而接口內部存儲的是指向這個複製品的指針,既沒法修改複製品的狀態,也沒法獲取指針
  • 只有當接口存儲的類型和對象都爲nil時,接口才等於nil
  • 接口調用不會作receiver的自動轉換
  • 接口一樣支持匿名字段方法
  • 接口也可實現相似OOP中的多態
  • 空接口能夠做爲任何類型數據的容器

示例:學習

package main


import "fmt"  

// 定義一個接口類型
type USB interface {
    Name() string
    Connect()
}

// 上邊的方法可使用下面的一種嵌入式修改
/*
type Connecter interface {
    Connect()
}

type USB interface {
    Name() string
    Connecter
}
*/

// 定義一個結構體
type PhoneConnecter struct {
  name string
}

// 定義一個類型爲結構體類型的方法
func (pc PhoneConnecter) Name() string {
    return pc.name
}

func (pc PhoneConnecter) Connect() {
    fmt.Println("Connect:", pc.name)
}

func Disconnect(usb USB) {
    if pc, ok := usb.(PhoneConnecter); ok {
        fmt.Println("Disconnect:", pc.name)
        return 
    }
    fmt.Println("Unknow device")
}


func main() {
    var a USB
    a = PhoneConnecter{"PhoneConnecter"}
    a.Connect()
    Disconnect(a)
}

打印:指針

➜  myfirstgo go run interface.go
Connect: PhoneConnecter
Disconnect: PhoneConnecter

2、反射reflection

一、基本概念

在運行時反射是程序檢查其所擁有的結構,尤爲是類型的一種能力;這是元編程的一種形式。它同時也是形成混淆的重要來源。code

二、特性

  • 反射能夠大大提升程序的靈活性,使得interface{}有更大的發揮餘地
  • 反射使用 TypeOfValueOf 函數從接口中獲取目標對象信息
  • 反射會將匿名字段做爲獨立字段(匿名字段本質)
  • 想要利用反射修改對象狀態,前提是 interface.data 是 settable,即 pointer-interface
  • 經過反射能夠「動態」調用方法

示例:視頻

package main


import (
    "fmt"
    "reflect"
)  

type User struct {
  Id int
  Name string
  Age int
}

func (u User) Hello() {
    fmt.Println("Hello, world")
}

func main() {
    u := User{1, "ok", 25}
    Info(u)
    
}

// 傳遞一個空接口
func Info(o interface{}) {
    t := reflect.TypeOf(o)
    fmt.Println("Type:", t.Name())

    v := reflect.ValueOf(o)
    fmt.Println("Fields:", v)

    // 獲取方法字段
    for i := 0; i < t.NumField(); i++ {
        f := t.Field(i)
        val := v.Field(i).Interface()
        fmt.Println("%6s: %v = %v\n", f.Name, f.Type, val)

    }

      // 獲取方法
     for i := 0; i < t.NumMethod(); i++ {
         m := t.Method(i)
         fmt.Println("%s: %v\n", m.Name, m.Type)
     }


}

打印結果:對象

➜  src go run myfirstgo/reflection.go
Type: User
Fields: {1 ok 25}
%6s: %v = %v
 Id int 1
%6s: %v = %v
 Name string ok
%6s: %v = %v
 Age int 25
%s: %v
 Hello func(main.User)
➜  src

反射匿名字段接口

package main


import (
    "fmt"
    "reflect"
)  

type User struct {
  Id int
  Name string
  Age int
}

// 匿名字段處理
type Manager struct {
    User
    title string
}

func main() {
    m := Manager{User: User{1, "Corwien", 18}, title:"123456"}
    t := reflect.TypeOf(m)

    // fmt.Println("%#v\n", t.Field(1))
    fmt.Println("%#v\n", t.FieldByIndex([]int{0,1}))
}

打印:

➜  src go run myfirstgo/reflection.go
%#v
 {Name  string  8 [1] false}

如何經過反射來進行方法的調用?

package main


import (
    "fmt"
    "reflect"
)  

type User struct {
  Id int
  Name string
  Age int
}

func (u User) Hello(name string) {
    fmt.Println("Hello", name, ", my name is", u.Name)
}

// 如何經過反射來進行方法的調用?

func main() {
    u := User{1, "ok", 25}
    v := reflect.ValueOf(u)
    mv := v.MethodByName("Hello")

    args := []reflect.Value{reflect.ValueOf("Corwien")}
    mv.Call(args)

}

打印:

➜  src go run myfirstgo/reflection.go
Hello Corwien , my name is ok
相關文章
相關標籤/搜索