go接口詳解


 

go面向接口編程知識點

 

接口定義與格式

接口(interface)是一種類型,用來定義行為(方法)。這句話有兩個重點,類型定義行為

首先解釋定義行為:
接口即一組方法定義的集合,定義了對象的一組行為,就是定義了一些函數,由具體的類型實例實現具體的方法。
換句話說,一個接口就是定義(規范或約束),接口並不會實現這些方法,具體的實現由類實現,實現接口的類必須嚴格按照接口的聲明來實現接口提供的所有功能。接口的作用應該是將定義與實現分離,降低耦合度。
在多人合作開發同一個項目時,​接口表示調用者和設計者的一種約定,事先定義好相互調用的接口可以大大提高開發的效率。有了接口,就可以在不影響現有接口聲明的情況下,修改接口的內部實現,從而使兼容性問題最小化。

接口的定義格式:

type Namer interface {
    Method1(param_list) return_type  //方法名(參數列表) 返回值列表
    Method2(param_list) return_type  //方法名(參數列表) 返回值列表
   ......
 }

  

隱式實現及實現條件

怎么實現接口:
實現接口的類並不需要顯式聲明,只需要實現接口所有的函數就表示實現了該接口,而且類還可以擁有自己的方法。

接口能被哪些類型實現:
接口可以被結構體實現,也可以被函數類型實現。
接口被實現的條件:
接口被實現的條件一:接口的方法與實現接口的類型方法格式一致(方法名、參數類型、返回值類型一致)。
接口被實現的條件二:接口中所有方法均被實現。

package main
 
import "fmt"
 
type Shaper interface {
    Area() float64
    //  Perimeter() float64
}
 
type Rectangle struct {
    length float64
    width  float64
}
 
// 實現 Shaper 接口中的方法
func (r *Rectangle) Area() float64 {
    return r.length * r.width
}
 
// Set 是屬於 Rectangle 自己的方法
func (r *Rectangle) Set(l float64, w float64) {
    r.length = l
    r.width = w
}
 
func main() {
    rect := new(Rectangle) //創建指針類型的結構體實例(類實例)
    rect.Set(2, 3)
    areaIntf := Shaper(rect) //這里將指針類型實例賦值給接口,下面會介紹。
    fmt.Printf("The rect has area: %f\n", areaIntf.Area())
}

  

接口賦值

現在來解釋接口是一個類型,本質是一個指針類型,那么什么樣的值可以賦值給接口,有兩種:實現了該接口的類或者接口

1.將對象賦值給接口
當接口實例中保存了自定義類型的實例后,就可以直接從接口上調用它所保存的實例的方法。

package main

import (
	"fmt"
)

//定義接口
type Testinterface interface{
	Teststring() string
	Testint() int
}

//定義結構體
type TestMethod struct{
	name string
	age int
}

//結構體的兩個方法隱式實現接口
func (t *TestMethod)Teststring() string{
	return t.name
}

func (t *TestMethod)Testint() int{
	return t.age
}

func main(){
	T1 := &TestMethod{"ling",34}
	T2 := TestMethod{"gos",43}
	//接口本質是一種類型
	//接口賦值:只要類實現了該接口的所有方法,即可將該類賦值給這個接口
	var Test1 Testinterface  //接口只能是值類型
	Test1 = T1   //TestMethod類的指針類型實例傳給接口
	fmt.Println(Test1.Teststring())
	fmt.Println(Test1.Testint())

	Test2 := T2   //TestMethod類的值類型實例傳給接口
	fmt.Println(Test2.Teststring())
	fmt.Println(Test2.Testint())
}

2.將接口賦值給另一個接口

1.只要兩個接口擁有相同的方法列表(與次序無關),即是兩個相同的接口,可以相互賦值
2.接口賦值只需要接口A的方法列表是接口B的子集(即假設接口A中定義的所有方法,都在接口B中有定義),那么B接口的實例可以賦值給A的對象。反之不成立,即子接口B包含了父接口A,因此可以將子接口的實例賦值給父接口。
3.即子接口實例實現了子接口的所有方法,而父接口的方法列表是子接口的子集,則子接口實例自然實現了父接口的所有方法,因此可以將子接口實例賦值給父接口。

 3.接口類型作為參數

第一點已經說了可以將實現接口的類賦值給接口,而將接口類型作為參數很常見。這時,那些實現接口的實例都能作為接口類型參數傳遞給函數/方法。

package main
import (
	"fmt"
)
//Shaper接口
type Shaper interface {
	Area() float64
}
// Circle struct結構體
type Circle struct {
	radius float64
}
// Circle類型實現Shaper中的方法Area()
func (c *Circle) Area() float64 {
	return 3.14 * c.radius * c.radius
}

func main() {
	// Circle的指針類型實例
	c1 := new(Circle)
	c1.radius = 2.5
	//將 Circle的指針類型實例c1傳給函數myArea,接收類型為Shaper接口
	myArea(c1)
}
func myArea(n Shaper) {
	fmt.Println(n.Area())
}

  

空接口

空接口是指沒有定義任何接口方法的接口。沒有定義任何接口方法,意味着Go中的任意對象都已經實現空接口(因為沒方法需要實現),只要實現接口的對象都可以被接口保存,所以任意對象都可以保存到空接口實例變量中

空接口的定義方式:

type empty_int interface {}

更常見的,會直接使用interface{}作為一種類型,表示空接口。例如:

// 聲明一個空接口實例
var i interface{}

再比如函數使用空接口類型參數:

func myfunc(i interface{})

如何使用空接口

可以定義一個空接口類型的array、slice、map、struct等,這樣它們就可以用來存放任意類型的對象,因為任意類型都實現了空接口。

package main

import "fmt"

func main() {
    any := make([]interface{}, 5)
    any[0] = 11
    any[1] = "hello world"
    any[2] = []int{11, 22, 33, 44}
    for _, value := range any {
        fmt.Println(value)
    }
}
11
hello world
[11 22 33 44]
<nil>
<nil>

通過空接口類型,Go也能像其它動態語言一樣,在數據結構中存儲任意類型的數據。

 

接口嵌套

接口可以嵌套,嵌套的內部接口將屬於外部接口,內部接口的方法也將屬於外部接口。

另外在類型嵌套時,如果內部類型實現了接口,那么外部類型也會自動實現接口,因為內部屬性是屬於外部屬性的。

type ReadWrite interface {
    Read(b Buffer) bool
    Write(b Buffer) bool
}
 
type Lock interface {
    Lock()
    Unlock()
}
 
type File interface {
  //ReadWrite為內部接口 ReadWrite
  //Lock為內部接口 Lock Close() }

  

 類型斷言

類型斷言為判斷一個類型有沒有實現接口。

假如我現在寫了一個結構體類型 MyFile 來實現上面的 File 接口,那么我如何知道 MyFile 是否實現了 File 接口呢?

package main
 
import "fmt"
 
type MyFile struct{}
 
func (m *MyFile) Read() bool {
    fmt.Printf("Read()\n")
    return true
}
 
// ...
// 假設我這里相繼實現了 Write(), Lock(),Unlock() 和 Close() 方法
 
func main() {
    my := new(MyFile)
    fIntf := File(my)
 
    // 看這里,看這里
    if v, ok := fIntf.(*MyFile); ok {
        v.Read()
    }
}  

類型斷言的格式:

if v, ok : = varI.(T) ; ok { 
   // checked type assertion
//do something return }

如果 v 是 varI 轉換到類型 T 的值,ok 會是 true;否則 v 是類型 T 的零值,ok 是 false。 

要是多個類型實現了同一個接口,比如前面的 areaIntf,要如何測試呢? 
那就要用 type-switch 來判斷了。

switch t := areaIntf.(type) {
case *Rectangle:
    // do something
case *Triangle:
    // do something
default:
    // do something
}

  

多態

1、多個類型(結構體)可以實現同一個接口。
2、一個類型(結構體)可以實現多個接口。
3、實現接口的類(結構體)可以賦值給接口。

package main

import "fmt"

type Shaper interface {
	Area() float64
}

// ==== Rectangle ====
type Rectangle struct {
	length float64
	width  float64
}

// 實現 Shaper 接口中的方法
func (r *Rectangle) Area() float64 {
	return r.length * r.width
}

// Set 是屬於 Rectangle 自己的方法
func (r *Rectangle) Set(l float64, w float64) {
	r.length = l
	r.width = w
}


// ==== Triangle ====
type Triangle struct {
	bottom float64
	hight  float64
}

func (t *Triangle) Area() float64 {
	return t.bottom * t.hight / 2
}

func (t *Triangle) Set(b float64, h float64) {
	t.bottom = b
	t.hight = h
}

// ==== Triangle End ====

func main() {
	rect := new(Rectangle)
	rect.Set(2, 3)
	areaIntf := Shaper(rect) //這種方法只能將指針類型的類示例賦值給接口
	fmt.Printf("The rect has area: %f\n", areaIntf.Area())

	triangle := new(Triangle)
	triangle.Set(2, 3)
	areaIntf = Shaper(triangle) //這種方法只能將指針類型的類示例賦值給接口
	fmt.Printf("The triangle has area: %f\n", areaIntf.Area())
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM