最早對interface的認知比較片面,很多人都說interface與channel是go語言的靈魂。然而在工作中使用的機會比較少,發現自己對interface的理解太片面。下面就記錄並總結下go中的interface。
interface是個啥
go程序設計中是這樣解釋的“接口是一種抽象類型,他並沒有暴露所包含數據的佈局或者內部結構,當然也沒有那些數據的基本操作,它所提供的僅僅是一些方法而已”。根據上面的說明我們可以提煉出接口是對一些方法的封裝。
很多的C++程序員吐槽go不便之處就是沒有’泛型’我們沒法使用模板啊,go語言中確實沒有,但是go提供了interface讓我們可以實現這種泛型。正如上面所說interface是一種抽象類型,是一組方法的集合,我們可以把它看成一種定義內部方法的動態數據類型,任意實現了這些方法的數據類型都可以認爲是特定的數據類型。(是不是有點像是C++中模板類)。
正如sort包中
package sort
// A type, typically a collection, that satisfies sort.Interface can be
// sorted by the routines in this package. The methods require that the
// elements of the collection be enumerated by an integer index.
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports 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)
}
在sort包中提供了三個方法,只要我們實現了這三個方法我們就可以對任何的類型進行排序。
看過了官方的sort是如何寫的我們也自己動手寫一個例子
package main
import "fmt"
type classmate struct {
class uint32
grade uint32
age uint32
name string
}
type info interface {
Len() int
Compare(agex,agei uint32)uint32
}
type listInfo []classmate
type listuint []uint32
func (this listInfo)Len()(int){
return len(this)
}
func (this listInfo)Compare(i,j uint32)(uint32){
if this[i].grade > this[j].grade{
return this[i].grade
}
return this[j].grade
}
func (this listuint)Len()(int){
return len(this)
}
func (this listuint)Compare(i,j uint32)(uint32){
if this[i] > this[j]{
return this[i]
}
return this[j]
}
func main(){
lInfo := listInfo{
classmate{1,1,6,"tom"},
classmate{2,3,8,"jim"},
}
fmt.Println("len is ", lInfo.Len())
garde := lInfo.Compare(0,1)
fmt.Printf("grade is %d \n",garde)
uInfo := listuint{9,6,8}
fmt.Println("len is ", uInfo.Len())
fmt.Printf("compare is %d ",uInfo.Compare(1,2))
}
示例中我們的interface中包含了兩個方法,len與compare,獲取長度與比較大小,我們分別爲不同的類型實現了接口中的方法,是不是有點像C++中的函數重載(C++中函數重載要求參數個數或者參數類型不同)。同時我們也發現我們爲不同的類型實現了方法,所以說一個接口類型不會去關心到底是什麼數據類型實現了他自身。接口類型的本質就是如果一個數據類型實現了自身的方法集,那麼該接口類型變量就能夠引用該數據類型的值。
在上面我們提到了sort包,我們只要實現了其提供的三個方法就可以對任何類型進行排序,因爲我們實現了它的接口類型。
package main
import "fmt"
type classmate struct {
class uint32
grade uint32
age uint32
name string
}
type classinfo struct {
total uint32
grade uint32
}
type info interface {
coutInfo()
changeInfo(grade uint32)
}
func (this *classmate)coutInfo(){
fmt.Println("info is ",this)
}
func (this *classmate)changeInfo(grade uint32){
this.grade = grade
fmt.Println("info is ",this)
}
func (this *classinfo)coutInfo(){
fmt.Println("info is ",this)
}
func (this *classinfo)changeInfo(grade uint32){
this.grade = grade
fmt.Println("info is ",this)
}
func interTest(test info,grade uint32){
test.coutInfo()
test.changeInfo(grade)
}
func main(){
mate := &classmate{
class: 1,
grade:1,
age:6,
name:"jim",
}
info := &classinfo{
total:30,
grade:5,
}
interTest(mate,3)
interTest(info,8)
}
輸出:
info is &{1 1 6 jim}
info is &{1 3 6 jim}
info is &{30 5}
info is &{30 8}
相信這個例子可以幫助我們更好的理解函數interTest的第一個輸入參數並沒有要求參數的具體類型,而是一個接口類型。
使用interface的注意事項
- 將對象賦值給接口變量時會複製該對象。
- 接口使用的是一個名爲itab的結構體存儲的
type iface struct{
tab *itab // 類型信息
data unsafe.Pointer // 實際對象指針
} - 只有接口變量內部的兩個指針都爲nil的時候,接口才等於nil。