[golang note] 接口使用

侵入式接口


        在其餘一些編程語言中,接口主要是做爲不一樣組件之間的契約存在,即規定雙方交互的規約。java

        對契約的實現是強制的,即必須確保用戶的確實現了該接口,而實現一個接口,須要從該接口繼承。ios

        若是一個類實現了接口A,即使另外一個接口B與A的方法列表相同,甚至鏈接口名都相同,但位於不一樣的命名空間下,那麼編譯器並不認爲這兩個接口是同樣的。c++

        所謂侵入的主要表如今於實現繼承接口的類須要明確聲明本身實現自某個接口。golang

        侵入式接口常糾結的問題是:應該提供哪些接口好呢?若是兩個類實現了相同的接口,應該把接口放到哪一個包好呢?編程

• c++接口示例

#include <string>
#include <iostream>
using namespace std;

class IPerson
{
public:
    IPerson() {}
    virtual ~IPerson() {}

public:
    virtual void          SetName(const string &name) = 0;
    virtual const string &GetName()                   = 0;
    virtual void          Work()                      = 0;
};

class Teacher : public IPerson
{
private:
    string m_Name;

public:
    Teacher() : m_Name("") {}
    virtual ~Teacher() {}

public:
    virtual void SetName(const string &name)
    {
        m_Name = name;
    }

    virtual const string &GetName()
    {
        return m_Name;
    }

    virtual void Work()
    {
        cout << "I'm a teacher" << endl;
    }
};

int main()
{
    IPerson *p = ::new Teacher;
    p->SetName("chris");
    p->Work();
    cout << p->GetName() << endl;
delete p;
return 1; }

• java接口示例

非侵入式接口


        在golang中,一個類只須要實現了某個接口中的全部函數,咱們就說這個類實現了該接口編程語言

        在golang中,一個類不須要繼承於接口,也不須要知道有哪些接口的存在。函數

        在golang中,接口和類之間再也不有所謂的契約關係,所以實現類的時候,只須要關心本身應該提供哪些方法,不用再糾結接口須要拆得多細才合理。接口由使用方按需定義,而不用事前規劃,也不須要繪製類庫的繼承樹圖。spa

        在golang中,不用爲了實現一個接口而導入一個包,由於多引用一個外部的包,就意味着更多的耦合。接口由使用方按自身需求來定義,使用方無需關心是否有其餘模塊定義過相似的接口。code

• 接口賦值

▶  對象實例賦值給接口

▪ 語法以下

// 接口定義
type IMyInterface1 interface {
    Func1(參數列表) (返回值列表)
    Func2(參數列表) (返回值列表)
    Func3(參數列表) (返回值列表)
}

type IMyInterface2 interface {
    Func1(參數列表) (返回值列表)
}

// 類定義
type MyClass struct {
    // 成員變量
}

func (p *MyClass) Func1(參數列表) (返回值列表) {
    // 函數體
}

func (p *MyClass) Func2(參數列表) (返回值列表) {
    // 函數體
}

func (p *MyClass) Func3(參數列表) (返回值列表) {
    // 函數體
}

func (p *MyClass) Func4(參數列表) (返回值列表) {
    // 函數體
}

// 接口賦值
var myInterface1 IMyInterface1 = new(MyClass)
var myInterface2 IMyInterface2 = new(MyClass)

// 接口調用
myInterface1.Func1(參數列表)
myInterface1.Func2(參數列表)
myInterface1.Func3(參數列表)

myInterface2.Func1(參數列表)

▪ 示例以下

package main

import "fmt" type IMyInterface1 interface {
    Func1() bool
    Func2() bool
    Func3() bool
}

type IMyInterface2 interface {
    Func1() bool
}

type MyClass struct {
}

func (p *MyClass) Func1() bool {
    fmt.Println("MyClass.Func1()")
    return true
}

func (p *MyClass) Func2() bool {
    fmt.Println("MyClass.Func2()")
    return true
}

func (p *MyClass) Func3() bool {
    fmt.Println("MyClass.Func3()")
    return true
}

func (p *MyClass) Func4() bool {
    fmt.Println("MyClass.Func4()")
    return true
}

func main() {
    var myInterface1 IMyInterface1 = new(MyClass) var myInterface2 IMyInterface2 = new(MyClass)

    myInterface1.Func1() // MyClass.Func1()
    myInterface1.Func2() // MyClass.Func2()
    myInterface1.Func3() // MyClass.Func3()

    myInterface2.Func1() // MyClass.Func1()
}

▶  接口之間賦值

         在golang中,只要兩個接口擁有相同的方法列表,那麼它們就是等同的,能夠相互賦值對象

         在golang中,等同的接口能夠分佈在不一樣的包中,包並非判斷接口是否等同的條件之一

         在golang中,接口賦值並不要求兩個接口必須等價。若是接口A的方法列表是接口B的方法列表的子集,那麼接口B是能夠賦值給接口A,可是反過來不成立

▪ 語法以下

// 接口定義
type IMyInterface1 interface {
    Func1(參數列表) (返回值列表)
    Func2(參數列表) (返回值列表)
}

type IMyInterface2 interface {
    Func1(參數列表) (返回值列表)
    Func2(參數列表) (返回值列表)
}

// 類定義
type MyClass struct {
    // 成員變量
}

func (p *MyClass) Func1(參數列表) (返回值列表) {
    // 函數體
}

func (p *MyClass) Func2(參數列表) (返回值列表) {
    // 函數體
}

func (p *MyClass) Func3(參數列表) (返回值列表) {
    // 函數體
}

// 接口賦值
var myInterface1 IMyInterface1 = new(MyClass)
var myInterface2 IMyInterface2 = myInterface1

// 接口調用
myInterface1.Func1(參數列表)
myInterface1.Func2(參數列表)

myInterface2.Func1(參數列表)
myInterface2.Func2(參數列表)

▪ 示例以下

package main

import "fmt" type IMyInterface1 interface {
    Func1() bool
    Func2() bool
}

type IMyInterface2 interface {
    Func1() bool
    Func2() bool
}

type IMyInterface3 interface {
    Func1() bool
}
type MyClass struct { } func (p *MyClass) Func1() bool { fmt.Println("MyClass.Func1()") return true } func (p *MyClass) Func2() bool { fmt.Println("MyClass.Func2()") return true } func (p *MyClass) Func3() bool { fmt.Println("MyClass.Func3()") return true } func main() { var myInterface1 IMyInterface1 = new(MyClass) var myInterface2 IMyInterface2 = myInterface1 // 等同接口
var myInterface3 IMyInterface3 = myInterface2 // 子集接口 myInterface1.Func1() // MyClass.Func1() myInterface1.Func2() // MyClass.Func2() myInterface2.Func1() // MyClass.Func1() myInterface2.Func2() // MyClass.Func2()

myInterface3.Func1() // MyClass.Func1() }

• 接口查詢

▶  接口查詢

         接口查詢功能一:檢查接口A指向的對象實例O是否實現了接口B,若是是則執行特定的代碼

         接口查詢功能二:檢查接口A指向的對象實例O是不是類型T,若是是則執行特定的代碼

         接口查詢是運行期行爲,編譯器編譯期不能肯定。

▪ 語法以下

// 接口定義
type IMyInterface1 interface {
    // 函數列表
}

type IMyInterface2 interface {
    // 函數列表
}

// 類定義
type MyClass struct {
    // 成員變量
}

// 接口賦值
var myInterface1 IMyInterface1 = new(MyClass)

// 查詢myInterface1指向的對象實例是否實現了IMyInterface2類型接口,若是實現,返回的myInterface2是指向對象的IMyInterface2類型接口實例
if myInterface2, ok := myInterface1.(IMyInterface2); ok {
    // 查詢成功,myInterface2爲IMyInterface2類型接口實例
}

// 查詢myInterface1指向的對象是不是MyClass類型實例,若是是,返回的myInstance是指向該對象的實例
if myInstance, ok := myInterface1.(*MyClass); ok{
    // 查詢成功,myInstance爲myInterface1指向的對象實例
}

▪ 示例以下

package main

import (
    "fmt"
)

type IMyInterface1 interface {
    Func1() bool Func2() bool
}

type IMyInterface2 interface {
    Func1() bool
}

type MyClass struct {
    Width, Height float64
}

func (p *MyClass) Func1() bool {
    fmt.Println("MyClass.Func1() - Width =", p.Width)
    return true
}

func (p *MyClass) Func2() bool {
    fmt.Println("MyClass.Func2() - Height =", p.Height)
    return true
}

func main() {
    var myInterface1 IMyInterface1 = &MyClass{100, 200}

    if myInterface2, ok := myInterface1.(IMyInterface2); ok {
        myInterface2.Func1() // MyClass.Func1() - Width = 100
    }

    if myInstance, ok := myInterface1.(*MyClass); ok {
        myInstance.Func1()             // MyClass.Func1() - Width = 100
        myInstance.Func2()             // MyClass.Func2() - Height = 200
        fmt.Println(myInstance.Width)  // 100
        fmt.Println(myInstance.Height) // 200
    }
}

▶  類型查詢

▪ 空接口:Any類型

         golang中空接口interface{}能夠指向任何對象實例。

         golang中空接口interface{}看起來像是能夠指向任何對象的Any類型,相似於COM中的IUnknown。

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var v1 interface{} = 1
    var v2 interface{} = "abc"
    var v3 interface{} = &v2
    var v4 interface{} = struct{ X int }{1}
    var v5 interface{} = &struct{ X int }{1}

    fmt.Println(reflect.TypeOf(v1)) // int
    fmt.Println(reflect.TypeOf(v2)) // string
    fmt.Println(reflect.TypeOf(v3)) // *interface {}
    fmt.Println(reflect.TypeOf(v4)) // struct { X int }
    fmt.Println(reflect.TypeOf(v5)) // *struct { X int }
}

▪ 類型查詢語法

         golang中使用type switch語句能夠查詢接口指向對象的真實數據類型。

var v interface{} = obj
switch instance := v.(type) { // 返回的instance是指向接口v指向的對象 case int:
        ...
    case string:
        ...
    ...
    default:
}

▪ 類型查詢示例

package main

import (
    "fmt"
)

type Stringer interface {
    String() string
}

type MyString struct {
    content string
}

func (str *MyString) String() string {
    return str.content
}

func PrintString(args ...interface{}) {
    for _, arg := range args {
        switch instance := arg.(type) {
        case string:
            fmt.Println(instance)
        default:
            if v, ok := arg.(Stringer); ok { // 若是對象具備Stringer接口
                fmt.Println(v.String())
            } else { // 不能打印的非字符串類型
                fmt.Println("unexpected string type")
            }
        }
    }
}

func main() {
    PrintString("abc")              // abc
    PrintString(&MyString{"hello"}) // hello
    PrintString(123)                // unexpected string type
}

• 接口組合

        相似struct的匿名組合,golang也爲接口提供了繼承機制,即接口也能夠匿名組合,只不過接口只包含方法,而不包含任何成員變量。

        接口組合與其餘高級編程語言中的表現一致,即接口A能夠被另外一個接口B繼承,造成接口繼承體系。 

▶  基本語法

type MyInterface1 interface {
    // 函數列表
}

type MyInterface2 interface {
    MyInterface1 // 函數列表
}

▶  繼承規則

        匿名組合的接口不可擁有同名函數,不然將給出錯誤:duplicate method XXXX

▪ 示例以下

package main

import (
    "fmt"
)

type MyInterface1 interface {
    Func1() bool
}

type MyInterface2 interface {
    //Func1() bool // duplicate method Func1
    Func2() bool
}

type MyInterface3 interface {
    MyInterface1
    MyInterface2 //Func1() bool // duplicate method Func1
    Func3() bool
}

type MyClass struct {
}

func (p *MyClass) Func1() bool {
    fmt.Println("MyClass.Func1()")
    return true
}

func (p *MyClass) Func2() bool {
    fmt.Println("MyClass.Func2()")
    return true
}

func (p *MyClass) Func3() bool {
    fmt.Println("MyClass.Func3()")
    return true
}

func main() {
    var myInterface3 MyInterface3 = new(MyClass)
    myInterface3.Func1() // MyClass.Func1()
    myInterface3.Func2() // MyClass.Func2()
    myInterface3.Func3() // MyClass.Func3()
}
相關文章
相關標籤/搜索