原來這纔是 Go Interface

定義

  • Interface 是一個定義了方法簽名的集合,用來指定對象的行爲,若是對象作到了 Interface 中方法集定義的行爲,那就能夠說實現了 Interface;git

  • 這些方法能夠在不一樣的地方被不一樣的對象實現,這些實現能夠具備不一樣的行爲;github

  • interface 的主要工做僅是提供方法名稱簽名,輸入參數,返回類型。最終由具體的對象來實現方法,好比 struct;golang

  • interface 初始化值爲 nil;安全

  • 使用 type 關鍵字來申明,interface 表明類型,大括號裏面定義接口的方法簽名集合。bash

    type Animal interface {
    	Bark() string
    	Walk() string
    }
    複製代碼

    以下,Dog 實現了 Animal 接口,因此能夠用 Animal 的實例去接收 Dog的實例,必須是同時實現 Bark() 和Walk() 方法,不然都不能算實現了Animal接口。ui

    type Dog struct {
    	name string
    }
    
    func (dog Dog) Bark() string {
    	fmt.Println(dog.name + ":wan wan wan!")
    	return "wan wan wan"
    }
    
    func (dog Dog) Walk() string {
    	fmt.Println(dog.name + ":walk to park!")
    	return "walk to park"
    }
    
    func main() {
    	var animal Animal
    
    	fmt.Println("animal value is:", animal)	//animal value is: <nil>
    	fmt.Printf("animal type is: %T\n", animal) //animal type is: <nil>
    
    	animal = Dog{"旺財"}
    	animal.Bark() //旺財:wan wan wan!
    	animal.Walk() //旺財:walk to park!
    
    	fmt.Println("animal value is:", animal) //animal value is: {旺財}
    	fmt.Printf("animal type is: %T\n", animal) //animal type is: main.Dog
    }
    複製代碼

nil interface

在上面的例子中,咱們打印剛定義的 animal:spa

  • value爲 nil
  • type 也爲 nil

官方定義:Interface values with nil underlying values:code

  • 只聲明沒賦值的interface 是nil interface,value和 type 都是 nil
  • 只要賦值了,即便賦了一個值爲nil類型,也再也不是nil interface
type I interface {
	Hello()
}

type S []int

func (i S) Hello() {
	fmt.Println("hello")
}
func main() {
	var i I
	fmt.Printf("1:i Type:%T\n", i)
	fmt.Printf("2:i Value:%v\n", i)

	var s S
	if s == nil {
		fmt.Printf("3:s Value%v\n", s)
		fmt.Printf("4:s Type is %T\n", s)
	}

	i = s
	if i == nil {
		fmt.Println("5:i is nil")
	} else {
		fmt.Printf("6:i Type:%T\n", i)
		fmt.Printf("7:i Value:%v\n", i)
	}
}
複製代碼

output:對象

1:i Type:<nil>
	2:i Value:<nil>
	3:s Value[]
	4:s Type is main.S
	6:i Type:main.S
	7:i Value:[]
複製代碼

從結果看,初始化的變量 i 是一個 nil interface,當把值爲 nil 的變量 s 賦值i後,i 再也不爲nil interface。
細心的同窗,會發現一個細節,輸出的第3行blog

3:s Value[]
複製代碼

明明,s的值是 nil,卻輸出的是一個[],這是因爲 fmt使用反射來肯定打印的內容,由於 s 的類型是slice,因此 fmt用 []來表示。

empty interface

Go 容許不帶任何方法的 interface ,這種類型的 interface 叫 empty interface。全部類型都實現了 empty interface,由於任何一種類型至少實現了 0 個方法。
典型的應用場景是 fmt包的Println方法,它能支持接收各類不一樣的類型的數據,而且輸出到控制檯,就是interface{}的功勞。下面咱們看下案例:

func Print(i interface{}) {
	fmt.Println(i)
}
func main() {
	var i interface{}
	i = "hello"
	Print(i)
	i = 100
	Print(i)
	i = 1.29
	Print(i)
}
複製代碼

Print 方法的參數類型爲 interface{},咱們傳入 string,int,float等類型它都能接收。
雖然interface{}能夠接收任何類型的參數,可是interface{}類型的 slice 是否是就能夠接受任何類型的 slice。以下代碼將會觸發 panic 錯誤,

var dataSlice []int = foo()
var interfaceSlice []interface{} = dataSlice
// cannot use dataSlice (type []int) as type []interface { } in assignment
複製代碼

具體緣由,官網 wiki(github.com/golang/go/w…) 有描述,大體含義是,致使錯誤是有兩個緣由的:

  • []interface{} 並非一個interface,它是一個slice,只是slice 中的元素是interface
  • []interface{} 類型的內存大小是在編譯期間就肯定的(N*2),而其餘切片類型的大小則爲 N * sizeof(MyType),所以不發快速的將類型[]MyType分配給 []interface{}。

判斷 interface 變量存儲的是哪一種類型

一個 interface 可被多種類型實現,有時候咱們須要區分 interface 變量究竟存儲哪一種類型的值?類型斷言提供對接口值的基礎具體值的訪問

t := i.(T)
複製代碼

該語句斷言接口值i保存的具體類型爲T,並將T的基礎值分配給變量t。若是i保存的值不是類型 T ,將會觸發 panic 錯誤。爲了不 panic 錯誤發生,能夠經過以下操做來進行斷言檢查

t, ok := i.(T)
複製代碼

斷言成功,ok 的值爲 true,斷言失敗 t 值爲T類型的零值,而且不會發生 panic 錯誤。

func main() {
	var i interface{}
	i = "hello"

	s := i.(string)
	fmt.Println(s)

	s, ok := i.(string)
	fmt.Println(s, ok)

	f, ok := i.(float64)
	fmt.Println(f, ok)

	i = 100
	t, ok := i.(int)
	fmt.Println(t, ok)

	t2 := i.(string) //panic
	fmt.Println(t2)
}
複製代碼

Type switch

還有一種方便的方法來判斷 interface 變量的具體類型,那就是利用 switch 語句。以下所示:

func Print(i interface{}) {
	switch i.(type) {
	case string:
		fmt.Printf("type is string,value is:%v\n", i.(string))
	case float64:
		fmt.Printf("type is float32,value is:%v\n", i.(float64))
	case int:
		fmt.Printf("type is int,value is:%v\n", i.(int))
	}
}
func main() {
	var i interface{}
	i = "hello"
	Print(i)
	i = 100
	Print(i)
	i = 1.29
	Print(i)
}
複製代碼

靈活高效的 interface 動態類型,使 Go 語言在保持強靜態類型的安全和高效的同時,也能靈活安全地在不一樣相容類型之間轉換

原文出於個人Github

參考

Golang print nil
InterfaceSlice

相關文章
相關標籤/搜索