What is Interface type in Go ?
GoLang官網language specification文檔對interface type的概念說明如下:
An interface type specifies a method set called its interface.
A variable of interface type can store a value of any type with a method set that is any superset of the interface.
Such a type is said to implement the interface. The value of an uninitialized variable of interface type is nil.
Go 語言提供了另外一種資料類型即介面(interface),它把所有的具有共性的方法定義在一起,這些方法只有函數簽名,沒有具體的實現代碼(類似於Java中的抽象函數),任何其他類型只要實現了介面中定義好的這些方法,那麼就說 這個類型實現(implement)了這個介面
介面的通用定義方式如下
/* 定義介面 */type interface_name interface { method_name1 [return_type] method_name2 [return_type] method_name3 [return_type] ... method_namen [return_type]}/* 定義xxx資料結構類型 */type struct_name xxx/* 實現介面方法 */func (struct_name_variable struct_name) method_name1() [return_type] { /* 方法實現 */} ...func (struct_name_variable struct_name) method_namen() [return_type] { /* 方法實現*/}
上面使用type聲明一個名為 interface_name 的介面,interface類型是可以定義為變數的,比如
var name interface_name
介面在Go語言中是參考型別,因此在上面定義的介面變數中,name是一個指標
我們依照上面的介面通用定義方式,定義以下執行個體
//定義電話介面type Phone interface { call()}//自訂結構體type Nokia struct {}//實現介面方法func (nokia Nokia)call() { fmt.Println("I am Nokia, I can call you!")}func main() { var phone Phone phone = new(Nokia) phone.call()}
call()是Phone介面定義好的一個方法,然後由Nokia實現了介面中這個方法,所以Nokia 實現了 Phone介面。
Interface“多態”特性執行個體
interface{}類型的變數,可以使用任何類型的值來賦值
func main() { var a interface{} = 123 var b interface{} = "abc" var c interface{} = 1.23 fmt.Println(a,b,c)}-----output----123 abc 1.23
在Go語言中內建的標準Packages中,有很多地方利用 interface 來處理未知資料類型,比如我們常用的fmt包,以fmt.Println為例,它的函數簽名格式如下
// Println formats using the default formats for its operands and writes to standard output.// Spaces are always added between operands and a newline is appended.// It returns the number of bytes written and any write error encountered.func Println(a ...interface{}) (n int, err error) { return Fprintln(os.Stdout, a...)}
fmt包的Println函數需要傳入interface類型的可變長參數,該函數在實現底層的列印行為時,要求傳入的可變長參數實現了fmt包中定義的Stringer介面。
Stringer介面類型描述如下:
// Stringer is implemented by any value that has a String method,// which defines the ``native'' format for that value.// The String method is used to print values passed as an operand// to any format that accepts a string or to an unformatted printer// such as Print.type Stringer interface { String() string}
所以,自訂類型想要調用fmt.Printf()做格式化列印,那隻需實現Stringer介面就行。
舉例說明:自訂輸出類型格式
定義一個map集合,並迴圈輸出map的元素
type PersonInfo struct { ID string Name string address string}func main() { //建立集合 var myMap map[string] PersonInfo //初始化集合 myMap = make(map[string]PersonInfo) //向map中添加元素 myMap ["1"] = PersonInfo{"1","zhangsan","shanghai"} myMap ["2"] = PersonInfo{"2","wangwu","beijing"} myMap ["3"] = PersonInfo{"3","lisi","tianjin"} //迴圈輸出 for num,person := range myMap{ fmt.Printf("%v: %v\n",num,person) }}----------output------------1: {1 zhangsan shanghai}2: {2 wangwu beijing}3: {3 lisi tianjin}
現在我們要自訂map集合輸出的格式,比如輸出第一個元素:1: {1&zhangsan&shanghai},因此 PersonInfo 需要實現Stringer介面
//自訂類型需要實現Stringer介面func (pInfo PersonInfo)String()string { return fmt.Sprintf("%v&%v&%v",pInfo.ID,pInfo.Name,pInfo.address)}
最終自訂格式化輸出為:
1: 1&zhangsan&shanghai2: 2&wangwu&beijing3: 3&lisi&tianjin
介面嵌入
Go語言的介面對嵌入支援的非常好,介面可以嵌入其他的介面,效果就像在介面中 直接添加被嵌入介面的方法一樣。
type HavingFoot interface { Foot()}type HavingEye interface { Eye()}type HuMan interface { HavingEye HavingFoot}
HuMan介面嵌入了 HavingFoot 介面與 HavingEye 介面,就相當於HuMan介面包含了HavingFoot 介面與 HavingEye 介面中所有的方法。如果要實現HuMan介面,就要定義 Foot() 與 Eye() 方法。
類型猜測
如果有兩個類型都實現了同一介面,那麼這兩個類型變數A,B都可以賦值給這個介面類型的變數C(類似於Java繼承中的向上傳遞),然後這個介面類型的變數C,可以去尋找是類型變數A還是B給它賦值的,查詢通用格式如下:
value,ok := obj.(struct)
struct:需要猜測的類型A或B value:傳回型別變數A或B
ok:查詢結果 obj:介面類型的變數C
類型猜測執行個體
//定義電話介面type Phone interface { call()}//自訂結構體type Nokia struct {}type Iphone struct {}//實現介面方法func (nokia Nokia)call() { fmt.Println("I am Nokia, I can call you!")}func (iphone Iphone)call() { fmt.Println("I am Iphone, I can call you!")}func main() { //將兩個子類賦值給介面 var phone1 Phone = Nokia{} var phone2 Phone = Iphone{} //判斷phone1是不是Nokia賦值給它的 if values,ok := phone1.(Nokia);ok { values.call() } //判斷phone2是不是Nokia賦值給它的 if values,ok := phone2.(Iphone);ok { values.call() }}
推薦閱讀:https://studygolang.com/articles/2652