接口 - Go 語言學習筆記

什麼是接口

Go接口是一種數據類型,它把全部的具備共性的方法定義在一塊兒,任何其餘類型只要實現了這些方法就是實現了這個接口。數組

在 go 語言中,接口有下面幾個特色:bash

  • 能夠包含0個或多個方法的簽名
  • 只定義方法的簽名,不包含實現
  • 實現接口不須要顯式的聲明,只需實現相應方法便可

接口的定義

利用關鍵字 interface 來定義一個接口,接口是一組方法的集合。
接口定義格式:函數

type 接口名稱 interface{
    方法名稱1(可能會用到的參數,可不傳) 返回類型
    方法名稱2(可能會用到的參數,可不傳) 返回類型
    ...
}
複製代碼

例如:ui

type People interface {
    Show(name string, age int) (id int, err error)  
    Set(name string, age int)
}
複製代碼

接口的實現

在 go 語言中,接口的實現是隱式的,不須要顯示聲明實現了接口,當一個類型爲接口中的全部方法提供定義時,它被稱爲實現該接口。
接口的實現示例以下:this

package main

import (
    "fmt"
)

type USB interface {
    Name() string
    Connect()
}

type PhoncConnecter struct {
    name string
}

// 由於如下方法 Name()、Connect() 與接口USB的方法相同,所以結構體PhoncConnecter實現了USB接口

func (pc PhoncConnecter) Name() string {
    return pc.name
}

func (pc PhoncConnecter) Connect() {
    fmt.Println(pc.name)
}

func main() {
    // 第一種直接在聲明結構時賦值
    var a USB   // 聲明一個接口類型的變量
    a = PhoncConnecter{"PhoneC"}    // 由於 PhoncConnecter 實現了接口 USB,因此能夠將該類型的實例賦給接口的變量
    a.Connect()

    // 第二種,先給結構賦值後在將值給接口去調用
    var b = PhoncConnecter{}
    b.name = "b"
    var c USB   // 聲明一個接口類型的變量
    c = b
    c.Connect()
}
複製代碼

輸出爲:spa

PhoneC
b
複製代碼

以上示例定義了USB的接口,並聲明瞭兩個方法 Name()、Connect(),接着定義了PhoncConnecter結構並聲明瞭一個name的變量,經過方法特性,對結構也一樣聲明瞭兩個方法Name()、Connect()。在GO中,只要實現了接口中定義的方法,默認就表明相似於其它語言中的繼承,繼承了那個接口,因此在main函數中,就能夠經過聲明接口和結構進行相對應的操做,從而達到代碼重複使用。code

接口的值類型

1. 接口的內部表現

一個接口能夠被認爲是由一個元組(類型,值)在內部表示的。type是接口的基礎具體類型,value是具體類型的值。對象

接口是一系列接口的集合,是一種抽象數據類型,接口變量能夠引用任何實現了接口的所有方法的具體數據類型的值。繼承

接口變量存儲了兩部分信息,一個是分配給接口變量的具體值(接口實現者的值),一個是值的類型的描述器(接口實現者的類型),形式是(value, concrete type),而不是(value, interface type)。接口

package main

import (
	"fmt"
)

type Test interface {
	Tester()
}

type MyFloat float64

func (m MyFloat) Tester() {
	fmt.Println(m)
}

func describe(t Test) {
	fmt.Printf("Interface 類型: %T , 值: %v\n", t, t)
}

func main() {
	var t Test
	f := MyFloat(89.7)
	t = f
	describe(t)
	t.Tester()
}
複製代碼

運行結果:

Interface 類型: main.MyFloat ,  值: 89.7
89.7
複製代碼

2. 空接口

空接口就是不包含任何方法的接口,它表示爲 interface {}。正由於如此,全部的類型都實現了空接口。

雖然空接口起不到任何做用,可是空接口在須要存儲任何類型數值的時候很是有用,由於空接口能夠存儲任意類型的數據。

package main

import (
	"fmt"
)

func describe(i interface{}) {
	fmt.Printf("Type = %T, value = %v\n", i, i)
}

func main() {
    // 任何類型的變量傳入均可以
    s := "Hello World"
    i := 55
    strt := struct {
        name string
    }{
        name: "Naveen R",
    }
    describe(s)
    describe(i)
    describe(strt)
}
複製代碼

運行結果:

Type = string, value = Hello World
Type = int, value = 55
Type = struct { name string }, value = {Naveen R}
複製代碼

3. 類型斷言

interface{} 可用於向函數傳遞任意類型的變量,但對於函數內部,該變量仍然爲 interface{} 類型(空接口類型),而不是傳入的實參類型。

接口類型向普通類型的轉換稱爲類型斷言(運行期肯定),類型斷言用於提取接口的基礎值,語法:i.(T)

func printArray(arr interface{}){
    // arr 是空接口,不是數組類型,報錯
    for _, v: = range arr{
        fmt.Print(v," ")
    }
  fmt.Println()
}
複製代碼

能夠經過類型斷言將接口類型轉換爲切片類型

func printArray(arr interface{}){
    // 經過斷言實現類型轉換
    a,_ := arr.([]int)
    for _,v:=range a{
        fmt.Println(v, " ")
    }
    fmt.Println()
}
複製代碼

在使用類型斷言時,最好判斷斷言是否成功

b,ok := a.(T)
if ok {
    ...
}
複製代碼

4. 類型判斷

類型判斷的語法相似於類型斷言,在類型斷言的語法i.(type)中,類型 type 應該由類型轉換的關鍵字 type 替換。 類型斷言能夠配合switch語句進行判斷:

package main

import (  
    "fmt"
)

func findType(i interface{}) {  
    switch i.(type) {
    case string:
        fmt.Printf("String: %s\n", i.(string))
    case int:
        fmt.Printf("Int: %d\n", i.(int))
    default:
        fmt.Printf("Unknown type\n")
    }
}
func main() {  
    findType("Naveen")
    findType(77)
    findType(89.98)
}
複製代碼

運行結果:

String: Naveen
Int: 77
Unknown type
複製代碼

接口的使用

1. 內嵌接口

一個接口能夠包含一個或多個其餘的接口,這至關於直接將這些內嵌接口的方法列舉在外層接口中同樣。
好比接口 File 包含了 ReadWrite 和 Lock 的全部方法,它還額外有一個 Close() 方法。

type ReadWrite interface {
    Read(b Buffer) bool
    Write(b Buffer) bool
}

type Lock interface {
    Lock()
    Unlock()
}

type File interface {
    ReadWrite
    Lock
    Close()
}
複製代碼

2. 接口變量賦值

能夠將一個實現接口的對象實例賦值給接口,也能夠將另一個接口賦值給接口。

  • 經過對象實例賦值
    若是一個類型實現了某個接口,能夠將類型的實例賦值給接口變量

  • 經過接口賦值
    接口A和接口B,若是接口A是接口B的子集,B類型的變量賦給A類型的變量

package main

import (
	"bytes"
	"io"
	"os"
	// "time"
)

func main() {
	// File、bytes.Buffer 實現了Writer
	var w io.Writer  // A
	w = os.Stdout
	w = new (bytes.Buffer)

	// w = time.Second  // error, 由於time.Second沒有實現了Writer

	var rwc io.ReadWriteCloser  // B
	rwc = os.Stdout // File 實現了 io.ReadWriteCloser 接口

	w = rwc
}
複製代碼

3. 用接口實現多態

go 語言多態是將實現接口的不一樣類型的實例賦給接口變量,讓接口的方法擁有不一樣的行爲。

package main

import "fmt"

type MyGreeting interface {
	Greet(name string) string
}

type EnglishGreeting struct {

}

type ChinesGreeting struct {

}

func (this *EnglishGreeting) Greet(name string) string {
	return "Hello " + name
}

func (this *ChinesGreeting) Greet(name string) string {
	return "你好 " + name
}

func main() {
	var greet MyGreeting = new(EnglishGreeting)
	fmt.Println(greet.Greet("Bill"))

	greet = new(ChinesGreeting)
	fmt.Println(greet.Greet("Bill"))
}
複製代碼

運行結果:

Hello Bill
你好 Bill
複製代碼
相關文章
相關標籤/搜索