Go 接口使用

本文來自:CSDN博客golang

感謝做者:fengfengdiandia編程

查看原文:go 接口markdown

Go 語言不是一種 「傳統」 的面向對象編程語言:它裏面沒有繼承的概念。數據結構

可是 Go 語言裏有很是靈活的接口概念,經過它能夠實現不少面向對象的特性。編程語言

接口 定義了一個 方法的集合,可是這些方法 不包含實現代碼,它們是 抽象的,接口裏也 不能包含變量測試

定義格式

定義接口的通常格式:ui

type Namer interface {
    Method1(param_list) return_type
    Method2(param_list) return_type
   ...
}

上面的 Namer 就是一個接口類型。spa

在 Go 語言中 接口 能夠有值, 一個 接口類型 的變量或一個 接口值var ai Namer ,ai 是一個 multiword 數據結構,它的值是 nil。 
它本質上是一個 指針,雖然不徹底是一回事。指向接口值的指針是非法的,會致使代碼錯誤。.net

類型(好比結構體)實現接口方法集中的方法,實現了 Namer 接口類型的變量能夠賦值給 ai,此時方法表中的指針會指向被實現的接口方法。3d

實現某個接口的類型,除了實現接口的方法外,還能夠有本身的方法。

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())
}

若是去掉 Shaper 中 Perimeter() float64 的註釋,編譯的時候會遇到下面的錯誤,這是由於 Rectangle 沒有實現 Perimeter() 方法。

cannot convert rect (type *Rectangle) to type Shaper: *Rectangle does not implement Shaper (missing Perimeter method)

多態

一、多個類型能夠實現同一個接口。 
二、一個類型能夠實現多個接口。

下面咱們增長一個類型 Triangle,一樣也爲它實現 Shaper 接口。

package main

import "fmt"

type Shaper interface {
    Area() float64
    //  Perimeter() 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
}

// ==== Rectangle End ====

// ==== 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())
}

  

靈活性

接口的定義是比較靈活的。

假設接口和類型處於不一樣的包中,只要類型實現了接口中的所有方法,那麼它就實現了此接口。

如今咱們將 Shaper 的定義放在 shaper 包 下, Rectangle 和 Triangle 的定義放在 test 包 下:

[root@ src]# tree
├── test
│   └── test.go
├── main.go
└── shaper
    └── shaper.go
shaper.go
package shaper

type Shaper interface {
    Area() float64
}
test.go
package test

// ==== 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
}

// ==== Rectangle End ====

// ==== 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 ====
// main.go
package main

import (
    "fmt"
    "shaper"
    "test"
)

func main() {
    rect := new(test.Rectangle)
    rect.Set(2, 3)
    areaIntf := shaper.Shaper(rect)
    fmt.Printf("The rect has area: %f\n", areaIntf.Area())

    triangle := new(test.Triangle)
    triangle.Set(2, 3)
    areaIntf = shaper.Shaper(triangle)
    fmt.Printf("The triangle has area: %f\n", areaIntf.Area())
}

如今運行 main.go 看看結果吧,嗯嗯,沒什麼問題,^_^

The rect has area: 6.000000 The triangle has area: 3.000000

 

接口嵌套

一個接口能夠包含一個或多個其餘的接口,這至關於直接將這些內嵌接口的方法列舉在外層接口中同樣。

好比接口 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()
}

  

類型斷言

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

一般咱們使用 類型斷言 來測試在某個時刻 varI 是否包含類型 T 的值:

if v, ok : = varI.(T) ; ok { // checked type assertion
    Process(v)
    return
}
// varI is not of type T

  

若是 v 是 varI 轉換到類型 T 的值,ok 會是 true;不然 v 是類型 T 的零值,ok 是 false

// main.go
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()
    }
}

輸出結果是:Read()

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

type-switch 類型判斷

switch t := areaIntf.(type) {
case *Rectangle:
    // do something
case *Triangle:
    // do something
default:
    // do something
}
相關文章
相關標籤/搜索