Go學習筆記(13)Go接口

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時,si轉換到類型T的值,ok的值是true;當接口變量i的實際類型不是T時,s是類型T的零值,okfalse。下面是一段檢測接口類型的實例:

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章