Go的接口
Go的接口定義了一組方法(方法集),但不包含這些方法的具體實現。接口提供了一種方式來說明某類對象具有的行爲,它的主要特點如下:
- 接口實際上就是一組方法聲明的集合,沒有具體實現,沒有字段屬性
- 某個類型只要實現了某個接口的所有方法,就實現了該接口,不需要顯示聲明實現了什麼接口
- 接口可以嵌入其它的接口
接口的定義和使用
定義接口的形式:
type InterfaceName interface {
Method1(param_list) return_type
Method2(param_list) return_type
}
下面是一個接口使用的實例,定義了一個Shaper
的接口,它包含了Area()
和Perimeter
兩個方法;再定義了一個Square
類型,它實現了上述具體兩個方法,從而繼承了接口。在主程序中,就可以創建一個Square
類型的變量賦值給接口Shaper
//定義接口
type Shaper interface {
Area() float64
Perimeter() float64
}
//定義類型正方形
type Square struct {
side float64
}
//實現接口中的方法Area
func (sq Square) Area() float64 {
return sq.side * sq.side
}
//實現接口中的方法Perimeter
func (sq Square) Perimeter() float64 {
return sq.side * 4
}
func main() {
var sq Shaper
sq = Square{
side: 2,
}
fmt.Println(sq.Area())
}
要注意的是,當對象賦值給接口時(sq=Square{side:2,}
),會對對象進行拷貝,再賦值給接口,接口中保存的是指向這個拷貝的地址指針。爲了性能考慮,我們也可以將對象的指針賦值給接口,這樣就不需要拷貝整個原始對象,只拷貝對象地址,地址還是指向原來的對象。上面的代碼可以改成:
//定義接口
type Shaper interface {
Area() float64
Perimeter() float64
}
//定義類型正方形
type Square struct {
side float64
}
//實現接口中的方法Area
func (sq *Square) Area() float64 {
return sq.side * sq.side
}
//實現接口中的方法Perimeter
func (sq *Square) Perimeter() float64 {
return sq.side * 4
}
func main() {
var sq Shaper
sq = &Square{
side: 2,
}
}
此外,還需要注意到幾點:
- 多個類型可以實現同一個接口
- 實現某個接口的類型除了實現接口要求實現的方法外,還可以有其它的方法
- 一個類型可以實現多個接口
在接口中內嵌接口
在Go接口的聲明中同樣可以嵌入一個接口,實現該外層接口的類型同樣需要實現內嵌接口聲明的方法
type Shaper interface {
Area() float64
Perimeter() float64
infoPrint //嵌入接口
}
type infoPrint interface {
PrintInfo()
}
//計算面積
func (sq Square) Area() float64 {
return sq.side * sq.side
}
//計算周長
func (sq Square) Perimeter() float64 {
return sq.side * 4
}
//實現內嵌接口的方法
func (sq Square) PrintInfo() {
fmt.Println("我是一個正方形")
}
func main() {
var sq Shaper
sq = Square{
side: 2,
}
}
接口轉換
不同的接口之間可以進行轉換,轉換的原則是大接口轉小接口,即將擁有內嵌子接口的接口轉換爲內嵌子接口
type Shaper interface {
Area() float64
Perimeter() float64
infoPrint //嵌入接口
}
type infoPrint interface {
PrintInfo()
}
//省略方法實現
func main(){
var sq Shaper
sq = Square{
side: 2,
}
var ip infoPrint
ip = infoPrint(sq) //這裏將Shaper轉換爲inforPrint
ip.PrintInfo()
}
空接口
如果一個接口中不含任何的方法,那麼該接口是個空接口,所有的類型都實現了空接口,它相當於所有類型的基類,類似於Java中的Object
類
type Empty interface {}
可以給一個空接口的變量賦值任何類型
類型斷言
一個接口類型的變量中可能包含着不同實際類型的值(實現接口的可以有不同的類型),當我們需要判斷接口變量中的實際類型時,可以使用類型斷言來檢測
假設i
是一個接口變量,T
是某個具體實現該接口的類型,那麼可以使用下面的語句來檢測i
是否爲類型T
:
s, ok := i.(T)
當接口變量i
的實際類型是T
時,s
是i
轉換到類型T
的值,ok
的值是true
;當接口變量i
的實際類型不是T
時,s
是類型T
的零值,ok
是false
。下面是一段檢測接口類型的實例:
type Shaper interface {
Area() float64
Perimeter() float64
}
type Square struct {
side float64
}
//計算面積
func (sq Square) Area() float64 {
return sq.side * sq.side
}
//計算周長
func (sq Square) Perimeter() float64 {
return sq.side * 4
}
func main(){
sq := Square{
side: 2,
}
isShape(sq)
}
//判斷接口中的數據類型
func isShape(s Shaper) {
if sp, ok := s.(Square); ok {
fmt.Println(sp, "is a shaper")
} else {
fmt.Println("none")
}
}
接口變量的類型也可以用一種type-switch
的形式去檢測:
type Shaper interface {
Area() float64
Perimeter() float64
}
type Square struct {
side float64
}
func (sq Square) Area() float64 {
return sq.side * sq.side
}
func (sq Square) Perimeter() float64 {
return sq.side * 4
}
func main(){
sq := Square{
side: 2,
}
whatType(sq)
}
func whatType(s interface{}) {
switch v := s.(type) {
case Square:
fmt.Println(v, "is a square")
case int:
fmt.Println(v, "is a square")
default:
fmt.Println("none")
}
}
在函數whatType
中,對接口變量的類型進行檢測,函數傳入的是一個空接口類型的參數,v = s.(type)
得到變量s
的具體類型,然後對case
中列舉的類型進行匹配,如果被檢測的類型沒有在case
語句列舉的類型中,就會執行default
語句。但注意type-switch
中不允許使用fallthrough