欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go之interface的具體使用

 更新時(shí)間:2018年03月10日 17:14:06   作者:飄飄白云  
這篇文章主要介紹了Go之interface的具體使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

淺顯地了解了一下 Go,發(fā)現(xiàn) Go 語法的設(shè)計(jì)非常簡潔,易于理解。正應(yīng)了 Go 語言之父 Rob Pike 說的那句“Less is more”—— 大道至簡。

下面就具體的語法特性說說我自己的體會。

interface

概覽

與通常以類型層次與繼承為根基的面向?qū)ο笤O(shè)計(jì)(OOP)語言(如C++、Java)不同,Go 的核心思想就是組合(composition)。Go 進(jìn)一步解耦了對象與操作,實(shí)現(xiàn)了真正的鴨子類型(Duck typing):一個(gè)對象如果能嘎嘎叫那就能當(dāng)做鴨子,而不是像 C++ 或 Java 那樣需要類型系統(tǒng)去保證:一個(gè)對象先得是只鴨子,然后才能嘎嘎叫。

type Duck interface {
  Quack()
}

type Animal struct {
  name string
}

func (animal Animal) Quack() {
  fmt.Println(animal.name, ": Quack! Quack! Like a duck!")
}

func main() {
  unknownAnimal := Animal{name: "Unknown"}

  var equivalent Duck
  equivalent = unknownAnimal
  equivalent.Quack()
}

運(yùn)行上面的代碼輸出:

Unknown : Quack! Quack! Like a duck!

下面用 Java 語言來實(shí)現(xiàn):

interface Duck {
  void Quack();
}

class SomeAnimal implements Duck {
  String name;

  public SomeAnimal(String name) {
    this.name = name;
  }

  public void Quack() {
    System.out.println(name + ": Quack! Quack! I am a duck!");
  }
}

public class Test {
  public static void main(String []args){
    SomeAnimal unknownAnimal = new SomeAnimal("Unknown");
    Duck equivalent = unknownAnimal;
    equivalent.Quack();
  }
}

兩相比較就能看出:Go 將對象與對其的操作(方法或函數(shù))解耦得更徹底。Go 并不需要一個(gè)對象通過類型系統(tǒng)來保證實(shí)現(xiàn)了某個(gè)接口(is a),而只需要這個(gè)對象實(shí)現(xiàn)了某個(gè)接口的方法即可(like a),而且類型聲明與方法聲明或?qū)崿F(xiàn)也是松耦合的形式。如果稍微轉(zhuǎn)換一下方法的實(shí)現(xiàn)方式:

func (animal Animal) Quack() {
  fmt.Println(animal.name, ": Quack! Quack! Like a duck!")
}

為:

func Quack(animal Animal) {
  fmt.Println(animal.name, ": Quack! Quack! Like a duck!")
}

是不是就和普通方法并無二致了?

在深入淺出 Cocoa 之消息一文中我曾分析過 Objective C 的消息調(diào)用過程:

Bird * aBird = [[Bird alloc] init];
[aBird fly];

中對 fly 的調(diào)用,編譯器通過插入一些代碼,將之轉(zhuǎn)換為對方法具體實(shí)現(xiàn) IMP 的調(diào)用,這個(gè) IMP 是通過在 Bird 的類結(jié)構(gòu)中的方法鏈表中查找名稱為 fly 的選擇子 SEL 對應(yīng)的具體方法實(shí)現(xiàn)找到的,編譯器會將消息調(diào)用轉(zhuǎn)換為對消息函數(shù) objc_msgSend的調(diào)用:

objc_msgSend(aBird, @selector(fly));

無論是 Objective C 的消息機(jī)制還是 Qt 中的 Signal/Slot 機(jī)制,可以說都是在嘗試將對象本身(數(shù)據(jù))與對對象的操作(消息)解耦,但 Go 將這個(gè)工作在語言層面做得更加徹底,這樣不僅避免多重繼承問題,還體現(xiàn)出面向?qū)ο笤O(shè)計(jì)中最要緊的事情:對象間的消息傳遞。

實(shí)現(xiàn)

interface 實(shí)際上就是一個(gè)結(jié)構(gòu)體,包含兩個(gè)成員。其中一個(gè)成員是指向具體數(shù)據(jù)的指針,另一個(gè)成員中包含了類型信息??战涌诤蛶Х椒ǖ慕涌诼杂胁煌?,下面分別是空接口和帶方法的接口是使用的數(shù)據(jù)結(jié)構(gòu):

struct Eface
{
  Type*  type;
  void*  data;
};
struct Iface
{
  Itab*  tab;
  void*  data;
};

struct Itab
{
  InterfaceType*  inter;
  Type*  type;
  Itab*  link;
  int32  bad;
  int32  unused;
  void  (*fun[])(void);
};

struct Type
{
  uintptr size;
  uint32 hash;
  uint8 _unused;
  uint8 align;
  uint8 fieldAlign;
  uint8 kind;
  Alg *alg;
  void *gc;
  String *string;
  UncommonType *x;
  Type *ptrto;
};

先看Eface,它是interface{}底層使用的數(shù)據(jù)結(jié)構(gòu)。數(shù)據(jù)域中包含了一個(gè)void*指針,和一個(gè)類型結(jié)構(gòu)體的指針。interface{}扮演的角色跟C語言中的void*是差不多的,Go中的任何對象都可以表示為interface{}。不同之處在于,interface{}中有類型信息,于是可以實(shí)現(xiàn)反射。

不同類型數(shù)據(jù)的類型信息結(jié)構(gòu)體并不完全一致,Type是類型信息結(jié)構(gòu)體中公共的部分,其中size描述類型的大小,UncommonType是指向一個(gè)函數(shù)指針的數(shù)組,收集了這個(gè)類型的具體實(shí)現(xiàn)的所有方法。

在reflect包中有個(gè)KindOf函數(shù),返回一個(gè)interface{}的Type,其實(shí)該函數(shù)就是簡單的取Eface中的Type域。

Iface和Eface略有不同,它是帶方法的interface底層使用的數(shù)據(jù)結(jié)構(gòu)。data域同樣是指向原始數(shù)據(jù)的,Itab中不僅存儲了Type信息,而且還多了一個(gè)方法表fun[]。一個(gè)Iface中的具體類型中實(shí)現(xiàn)的方法會被拷貝到Itab的fun數(shù)組中。

Type的UncommonType中有一個(gè)方法表,某個(gè)具體類型實(shí)現(xiàn)的所有方法都會被收集到這張表中。reflect包中的Method和MethodByName方法都是通過查詢這張表實(shí)現(xiàn)的。表中的每一項(xiàng)是一個(gè)Method,其數(shù)據(jù)結(jié)構(gòu)如下:

struct Method
{
  String *name;
  String *pkgPath;
  Type  *mtyp;
  Type *typ;
  void (*ifn)(void);
  void (*tfn)(void);
};

Iface的Itab的InterfaceType中也有一張方法表,這張方法表中是接口所聲明的方法。其中每一項(xiàng)是一個(gè)IMethod,數(shù)據(jù)結(jié)構(gòu)如下:

struct IMethod
{
  String *name;
  String *pkgPath;
  Type *type;
};

 跟上面的Method結(jié)構(gòu)體對比可以發(fā)現(xiàn),這里是只有聲明沒有實(shí)現(xiàn)的。

Iface中的Itab的func域也是一張方法表,這張表中的每一項(xiàng)就是一個(gè)函數(shù)指針,也就是只有實(shí)現(xiàn)沒有聲明。

類型轉(zhuǎn)換時(shí)的檢測就是看Type中的方法表是否包含了InterfaceType的方法表中的所有方法,并把Type方法表中的實(shí)現(xiàn)部分拷到Itab的func那張表中。

注意事項(xiàng)

一個(gè)interface在沒有進(jìn)行初始化時(shí),對應(yīng)的值是nil。也就是說:

var v interface{}

此時(shí)v就是一個(gè)nil。在底層存儲上,它是一個(gè)空指針。

與之不同的情況

var obj *T
var v interface{}
v = obj

此時(shí)v是一個(gè)interface,它的值是nil,也就是說其data域?yàn)榭?,但它自身不為nil。

下面來看個(gè)例子就明白了:
Go語言中的error類型實(shí)際上是抽象了Error()方法的error接口:

type error interface {
  Error() string
}

有如下代碼:

type Error struct {
  errCode uint8
}

func (e *Error) Error() string {
  switch e.errCode {
  default:
    return "unknown error"
  }
}

func test_checkError() {
  var e *Error
  if e == nil {
    fmt.Println("e is nil")
  } else {
    fmt.Println("e is not nil")
  }

  var err error
  err = e

  if err == nil {
    fmt.Println("err is nil")
  } else {
    fmt.Println("err is not nil")
  }
}

運(yùn)行test_checkError()輸出:

e is nil
err is not nil

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • golang struct 實(shí)現(xiàn) interface的方法

    golang struct 實(shí)現(xiàn) interface的方法

    這篇文章主要介紹了golang struct 實(shí)現(xiàn) interface的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-07-07
  • Golang 實(shí)現(xiàn)Socket服務(wù)端和客戶端使用TCP協(xié)議通訊

    Golang 實(shí)現(xiàn)Socket服務(wù)端和客戶端使用TCP協(xié)議通訊

    這篇文章主要介紹了Golang 實(shí)現(xiàn)Socket服務(wù)端和客戶端使用TCP協(xié)議通訊,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • golang雙指針快速排序的實(shí)現(xiàn)代碼

    golang雙指針快速排序的實(shí)現(xiàn)代碼

    這篇文章主要介紹了golang雙指針快速排序的實(shí)現(xiàn)代碼,通過實(shí)例代碼補(bǔ)充介紹了Golang實(shí)現(xiàn)快速排序和歸并排序以及堆排序算法全注釋,需要的朋友可以參考下
    2024-03-03
  • Golang使用singleflight解決并發(fā)重復(fù)請求

    Golang使用singleflight解決并發(fā)重復(fù)請求

    高并發(fā)的場景下,經(jīng)常會出現(xiàn)并發(fā)重復(fù)請求資源的情況,singleflight是golang內(nèi)置的一個(gè)包,這個(gè)包提供了對重復(fù)函數(shù)調(diào)用的抑制功能,所以下面我們就來看看如何使用它解決并發(fā)重復(fù)請求吧
    2023-08-08
  • golang如何通過viper讀取config.yaml文件

    golang如何通過viper讀取config.yaml文件

    這篇文章主要介紹了golang通過viper讀取config.yaml文件,圍繞golang讀取config.yaml文件的相關(guān)資料展開詳細(xì)內(nèi)容,需要的小伙伴可以參考一下
    2022-03-03
  • 使用 go 實(shí)現(xiàn)多線程下載器的方法

    使用 go 實(shí)現(xiàn)多線程下載器的方法

    本篇文章帶領(lǐng)大家學(xué)習(xí)使用go實(shí)現(xiàn)一個(gè)簡單的多線程下載器,給她家詳細(xì)介紹了多線程下載原理及實(shí)例代碼,感興趣的朋友跟隨小編一起看看吧
    2021-10-10
  • go中string、int、float相互轉(zhuǎn)換的實(shí)現(xiàn)示例

    go中string、int、float相互轉(zhuǎn)換的實(shí)現(xiàn)示例

    本文主要介紹了go中string、int、float相互轉(zhuǎn)換的實(shí)現(xiàn)示例,文中根據(jù)實(shí)例編碼詳細(xì)介紹的十分詳盡,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 舉例講解Go語言中函數(shù)的閉包使用

    舉例講解Go語言中函數(shù)的閉包使用

    這篇文章主要介紹了Go語言中函數(shù)的閉包使用示例,函數(shù)閉包c(diǎn)losure是編程語言中十分重要的特性,需要的朋友可以參考下
    2016-03-03
  • Golang動態(tài)數(shù)組的實(shí)現(xiàn)示例

    Golang動態(tài)數(shù)組的實(shí)現(xiàn)示例

    動態(tài)數(shù)組能自動調(diào)整大小,與靜態(tài)數(shù)組不同,其大小不固定,可根據(jù)需求變化,實(shí)現(xiàn)通常依賴于數(shù)據(jù)結(jié)構(gòu)如鏈表或數(shù)組加額外信息,本文就來介紹一下Golang動態(tài)數(shù)組的實(shí)現(xiàn)示例,感興趣的可以了解一下
    2024-10-10
  • golang?對象深拷貝的常見方式及性能

    golang?對象深拷貝的常見方式及性能

    這篇文章主要介紹了golang?對象深拷貝的常見方式及性能,Go語言中所有賦值操作都是值傳遞,如果結(jié)構(gòu)中不含指針,則直接賦值就是深度拷貝,文章圍繞主題展開更多相關(guān)資料,需要的小伙伴可以參考一下
    2022-06-06

最新評論