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

Golang中interface的基本用法詳解

 更新時間:2023年01月04日 10:56:55   作者:rubys_  
Go 中接口也是一個使用得非常頻繁的特性,好的軟件設(shè)計往往離不開接口的使用,比如依賴倒置原則(通過抽象出接口,分離了具體實現(xiàn)與實際使用的耦合)。 今天,就讓我們來了解一下 Go 中接口的一些基本用法

Go 中接口也是一個使用得非常頻繁的特性,好的軟件設(shè)計往往離不開接口的使用,比如依賴倒置原則(通過抽象出接口,分離了具體實現(xiàn)與實際使用的耦合)。 今天,就讓我們來了解一下 Go 中接口的一些基本用法。

概述

Go 中的接口跟我們常見的編程語言中的接口不太一樣,go 里面實現(xiàn)接口是不需要使用 implements 關(guān)鍵字顯式聲明的, go 的接口為我們提供了難以置信的一系列的靈活性和抽象性。接口有兩個特點:

  • 接口本質(zhì)是一種自定義類型。(跟 Java 中的接口不一樣)
  • 接口是一種特殊的自定義類型,其中沒有數(shù)據(jù)成員,只有方法(也可以為空)。

go 中的接口定義方式如下:

type?Flyable?interface?{
????Fly()?string
}

接口是完全抽象的,不能將其實例化。但是我們創(chuàng)建變量的時候可以將其類型聲明為接口類型:

var?a?Flyable

然后,對于接口類型變量,我們可以把任何實現(xiàn)了接口所有方法的類型變量賦值給它,這個過程不需要顯式聲明。 例如,假如 Bird 實現(xiàn)了 Fly 方法,那么下面的賦值就是合法的:

//?Bird?實現(xiàn)了?Flyable?的所有方法
var?a?Flyable?=?Bird{}

go 實現(xiàn)接口不需要顯式聲明。

由此我們引出 go 接口的最重要的特性是:

  • 只要某個類型實現(xiàn)了接口的所有方法,那么我們就說該類型實現(xiàn)了此接口。該類型的值可以賦給該接口的值。

  • 因為 interface{} 沒有任何方法,所以任何類型的值都可以賦值給它(類似 Java 中的 Object)

基本使用

Java 中的 interface(接口)

先看看其他語言中的 interface 是怎么使用的。

我們知道,很多編程語言里面都有 interface 這個關(guān)鍵字,表示的是接口,應(yīng)該也用過,比如 Java 里面的:

//?定義一個?Flyable?接口
interface?Flyable?{
????public?void?fly();
}

//?定義一個名為?Bird?的類,顯式實現(xiàn)了?Flyable?接口
class?Bird??implements?Flyable?{
????public?void?fly()?{
????????System.out.println("Bird?fly.");
????}
}

class?Test?{
????//?fly?方法接收一個實現(xiàn)了?Flyable?接口的類
????public?static?void?fly(Flyable?flyable)?{
????????flyable.fly();
????}

????public?static?void?main(String[]?args)?{
????????Bird?b?=?new?Bird();
????????//?b?實現(xiàn)了?Flyable?接口,所以可以作為?fly?的參數(shù)
????????fly(b);
????}
}

在這個例子中,我們定義了一個 Flyable 接口,然后定義了一個實現(xiàn)了 Flyable 接口的 Bird 類, 最后,定義了一個測試的類,這個類的 fly 方法接收一個 Flyable 接口類型的參數(shù), 因為 Bird 類實現(xiàn)了 Flyable 接口,所以可以將 b 作為參數(shù)傳遞給 fly 方法。

這個例子就是 Java 中 interface 的典型用法,如果一個類要實現(xiàn)一個接口,我們必須顯式地通過 implements 關(guān)鍵字來聲明。 然后使用的時候,對于需要某一接口類型的參數(shù)的方法,我們可以傳遞實現(xiàn)了那個接口的對象進去。

Java 中類實現(xiàn)接口必須顯式通過 implements 關(guān)鍵字聲明。

go 中的 interface(接口)

go 里面也有 interface 這個關(guān)鍵字,但是 go 與其他語言不太一樣。 go 里面結(jié)構(gòu)體與接口之間不需要顯式地通過 implements 關(guān)鍵字來聲明的,在 go 中,只要一個結(jié)構(gòu)體實現(xiàn)了 interface 的所有方法,我們就可以將這個結(jié)構(gòu)體當(dāng)做這個 interface 類型,比如下面這個例子:

package?main

import?"fmt"

//?定義一個?Flyable?接口
type?Flyable?interface?{
?Fly()?string
}

//?Bird?結(jié)構(gòu)體沒有顯式聲明實現(xiàn)了?Flyable?接口(沒有?implements?關(guān)鍵字)
//?但是?Bird?定義了?Fly()?方法,
//?所以可以作為下面?fly?函數(shù)的參數(shù)使用。
type?Bird?struct?{
}

func?(b?Bird)?Fly()?string?{
?return?"bird?fly."
}

//?只要實現(xiàn)了?Flyable?的所有方法,
//?就可以作為?output?的參數(shù)。
func?fly(f?Flyable)?{
?fmt.Println(f.Fly())
}

func?main()?{
?var?b?=?Bird{}
?//?在?go?看來,b?實現(xiàn)了?Fly?接口,
?//?因為?Bird?里面實現(xiàn)了?Fly?接口的所有方法。
?fly(b)
}

在上面這個例子中,Person 結(jié)構(gòu)體實現(xiàn)了 Stringer 接口的所有方法,所以在需要 Stringer 接口的地方,都可以用 Person 的實例作為參數(shù)。

Go 中結(jié)構(gòu)體實現(xiàn)接口不用通過 implements 關(guān)鍵字聲明。(實際上,Go 也沒有這個關(guān)鍵字)

go interface 的優(yōu)勢

go 接口的這種實現(xiàn)方式,有點類似于動態(tài)類型的語言,比如 Python,但是相比 Python,go 在編譯期間就可以發(fā)現(xiàn)一些明顯的錯誤。

比如像 Python 中下面這種代碼,如果傳遞的 coder 沒有 say_hello 方法,這種錯誤只有運行時才能發(fā)現(xiàn):

def?hello_world(coder):
????coder.say_hello()

但如果是 go 的話,下面這種寫法中,如果傳遞給 hello_world 沒有實現(xiàn) say 接口,那么編譯的時候就會報錯,無法通過編譯:

type?say?interface?{
?say_hello()
}

func?hello_world(coder?say)?{
?coder.say_hello()
}

因此,go 的這種接口實現(xiàn)方式有點像動態(tài)類型的語言,在一定程度上給了開發(fā)者自由,但是也在語言層面幫開發(fā)者做了類型檢查。

go 中不必像靜態(tài)類型語言那樣,所有地方都明確寫出類型,go 的編譯器幫我們做了很多工作,讓我們在寫 go 代碼的時候更加的輕松。 interface 也是,我們無需顯式實現(xiàn)接口,只要我們的結(jié)構(gòu)體實現(xiàn)了接口的所有類型,那么它就可以當(dāng)做那個接口類型使用(duck typing)。

空接口

go 中的 interface{} 表示一個空接口(在比較新版本中也可以使用 any 關(guān)鍵字來代替 interface{}),這個接口沒有任何方法。因此可以將任何變量賦值給 interface{} 類型的變量。

這在一些允許不同類型或者不確定類型參數(shù)的方法中用得比較廣泛,比如 fmt 里面的 println 等方法。

如何使用 interface{} 類型的參數(shù)?

這個可能是大部分人所需要關(guān)心的地方,因為這可能在日常開發(fā)中經(jīng)常需要用到。

類型斷言

當(dāng)實際開發(fā)中,我們接收到一個接口類型參數(shù)的時候,我們可能會知道它是幾種可能的情況之一了,我們就可以使用類型斷言來判斷 interface{} 變量是否實現(xiàn)了某一個接口:

func?fly(f?interface{})?{
?//?第一個返回值?v?是?f?轉(zhuǎn)換為接口之前的值,
?//?ok?為?true?表示?f?是?Bird?類型
?if?v,?ok?:=?f.(Flyable);?ok?{
??fmt.Println("bird?"?+?v.Fly())
?}

?//?斷言形式:接口.(類型)
?if?_,?ok?:=?f.(Bird);?ok?{
??fmt.Println("bird?flying...")
?}
}

在實際開發(fā)中,我們可以使用 xx.(Type) 這種形式來判斷:

  • interface{} 類型的變量是否是某一個類型
  • interface{} 類型的變量是否實現(xiàn)了某一個接口

如,f.(Flyable) 就是判斷 f 是否實現(xiàn)了 Flyable 接口,f.(Bird) 就是判斷 f 是否是 Bird 類型。

另外一種類型斷言方式

可能我們會覺得上面的那種 if 的判斷方式有點繁瑣,確實如此,但是如果我們不能保證 f 是某一類型的情況下,用上面這種判斷方式是比較安全的。

還有另外一種判斷方式,用在我們確切地知道 f 具體類型的情況:

func?fly2(f?interface{})?{
?fmt.Println("bird?"?+?f.(Flyable).Fly())
}

在這里,我們斷言 f 是 Flyable 類型,然后調(diào)用了它的 Fly 方法。

這是一種不安全的調(diào)用,如果 f 實際上沒有實現(xiàn)了 Flyable 接口,上面這行代碼會引發(fā) panic。 而相比之下,v, ok := f.(Flyable) 這種方式會返回第二個值讓我們判斷這個斷言是否成立。

switch...case 中判斷接口類型

除了上面的斷言方式,還有另外一種判斷 interface{} 類型的方法,那就是使用 switch...case 語句:

func?str(f?interface{})?string?{
?//?判斷?f?的類型
?switch?f.(type)?{
?case?int:
??//?f?是?int?類型
??return?"int:?"?+?strconv.Itoa(f.(int))
?case?int64:
??//?f?是?int64?類型
??return?"int64:?"?+?strconv.FormatInt(f.(int64),?10)
????case?Flyable:
????????return?"flyable..."
?}
?return?"???"
}

編譯器自動檢測類型是否實現(xiàn)接口

上面我們說過了,在 go 里面,類型不用顯式地聲明實現(xiàn)了某個接口(也不能)。那么問題來了,我們開發(fā)的時候, 如果我們就是想讓某一個類型實現(xiàn)某個接口的時候,但是漏實現(xiàn)了一個方法的話,IDE 是沒有辦法知道我們漏了的那個方法的:

type?Flyable?interface?{
?Fly()?string
}

//?沒有實現(xiàn)?Flyable?接口,因為沒有?Fly()?方法
type?Bird?struct?{
}

func?(b?Bird)?Eat()?string?{
?return?"eat."
}

比如這段代碼中,我們本意是要 Bird 也實現(xiàn) Fly 方法的,但是因為沒有顯式聲明,所以 IDE 沒有辦法知道我們的意圖。 這樣一來,在實際運行的時候,那些我們需要 Flyable 的地方,如果我們傳了 Bird 實例的話,就會報錯了。

一種簡單的解決方法

如果我們明確知道 Bird 將來是要當(dāng)做 Flyable 參數(shù)使用的話,我們可以加一行聲明:

var?_?Flyable?=?Bird{}

這樣一來,因為我們有 Bird 轉(zhuǎn) Flyable 類型的操作,所以編譯器就會去幫我們檢查 Bird 是否實現(xiàn)了 Flyable 接口了。 如果 Bird 沒有實現(xiàn) Flyable 中的所有方法,那么編譯的時候會報錯,這樣一來,這些錯誤就不用等到實際運行的時候才能發(fā)現(xiàn)了。

實際上,很多開源項目都能看到這種寫法。看起來定義了一個空變量,但是實際上確可以幫我們進行類型檢查。

這種解決方法還有另外一種寫法如下:

var?_?Flyable?=?(*Bird)(nil)

類型轉(zhuǎn)換與接口斷言

我們知道了,接口斷言可以獲得一個具體類型(也可以是接口)的變量,同時我們也知道了,在 go 里面也有類型轉(zhuǎn)換這東西, 實際上,接口斷言與類型轉(zhuǎn)換都是類型轉(zhuǎn)換,它們的差別只是:

interface{} 只能通過類型斷言來轉(zhuǎn)換為某一種具體的類型,而一般的類型轉(zhuǎn)換只是針對普通類型之間的轉(zhuǎn)換。

//?類型轉(zhuǎn)換:f?由?float32?轉(zhuǎn)換為?int
var?f?float32?=?10.8
i?:=?int(f)

//?接口的類型斷言
var?f?interface{}
v,?ok?:=?f.(Flyable)

如果是 interface{},需要使用類型斷言轉(zhuǎn)換為某一具體類型。

一個類型可以實現(xiàn)多個接口

上文我們說過了,只要一個類型實現(xiàn)了接口中的所有方法,那么那個類型就可以當(dāng)作是那個接口來使用:

type?Writer?interface?{
????Write(p?[]byte)?(n?int,?err?error)
}

type?Closer?interface?{
????Close()?error
}

type?myFile?struct?{
}

//?實現(xiàn)了?Writer?接口
func?(m?myFile)??Write(p?[]byte)?(n?int,?err?error)?{
?return?0,?nil
}

//?實現(xiàn)了?Closer?接口
func?(m?myFile)?Close()?error?{
?return?nil
}

在上面這個例子中,myFile 實現(xiàn)了 Write 和 Close 方法,而這兩個方法分別是 Writer 和 Closer 接口中的所有方法。 在這種情況下,myFile 的實例既可以作為 Writer 使用,也可以作為 Closer 使用:

func?foo(w?Writer)?{
?w.Write([]byte("foo"))
}

func?bar(c?Closer)?{
?c.Close()
}

func?test()?{
?m?:=?myFile{}
?//?m?可以作為?Writer?接口使用
?foo(m)
?//?m?也可以作為?Closer?接口使用
?bar(m)
}

接口與 nil 不相等

有時候我們會發(fā)現(xiàn),明明傳了一個 nil 給 interface{} 類型的參數(shù),但在我們判斷實參是否與 nil 相等的時候,卻發(fā)現(xiàn)并不相等,如下面這個例子:

func?test(i?interface{})?{
?fmt.Println(reflect.TypeOf(i))
?fmt.Println(i?==?nil)
}

func?main()?{
?var?b?*int?=?nil
?test(b)?//?會輸出:*int?false
?test(nil)?//?會輸出:<nil>?true
}

這是因為 go 里面的 interface{} 實際上是包含兩部分的,一部分是 type,一部分是 data,如果我們傳遞的 nil 是某一個類型的 nil, 那么 interface{} 類型的參數(shù)實際上接收到的值會包含對應(yīng)的類型。 但如果我們傳遞的 nil 就是一個普通的 nil,那么 interface{} 類型參數(shù)接收到的 type 和 data 都為 nil, 這個時候再與 nil 比較的時候才是相等的。

嵌套的接口

在 go 中,不僅結(jié)構(gòu)體與結(jié)構(gòu)體之間可以嵌套,接口與接口也可以通過嵌套創(chuàng)造出新的接口。

type?Writer?interface?{
????Write(p?[]byte)?(n?int,?err?error)
}

type?Closer?interface?{
????Close()?error
}

//?下面這個接口包含了?Writer?和?Closer?的所有方法
type?WriteCloser?interface?{
????Writer
????Closer
}

WriteCloser 是一個包含了 Writer 和 Closer 兩個接口所有方法的新接口,也就是說,WriteCloser 包含了 Write 和 Close 方法。

這樣的好處是,可以將接口拆分為更小的粒度。比如,對于某些只需要 Close 方法的地方,我們就可以用 Closer 作為參數(shù)的類型, 即使參數(shù)也實現(xiàn)了 Write 方法,因為我們并不關(guān)心除了 Close 以外的其他方法:

func?foo(c?Closer)?{
?//?...
?c.Close()
}

而對于上面的 myFile,因為同時實現(xiàn)了 Writer 接口和 Closer 接口,而 WriteCloser 包含了這兩個接口, 所以實際上 myFile 可以當(dāng)作 WriteCloser 或者 Writer 或 Closer 類型使用。

總結(jié)

  • 接口里面只聲明了方法,沒有數(shù)據(jù)成員。
  • go 中的接口不需要顯式聲明(也不能)。
  • 只要一個類型實現(xiàn)了接口的所有方法,那么該類型實現(xiàn)了此接口。該類型的值可以賦值給該接口類型。
  • interface{}/any 是空接口,任何類型的值都可以賦值給它。
  • 通過類型斷言我們可以將 interface{} 類型轉(zhuǎn)換為具體的類型。
  • 我們通過聲明接口類型的 _ 變量來讓編譯器幫我們檢查我們的類型是否實現(xiàn)了某一接口。
  • 一個類型可以同時實現(xiàn)多個接口,可以當(dāng)作多個接口類型來使用。
  • nil 與值為 nil 的 interface{} 實際上不想等,需要注意。
  • go 中的接口可以嵌套,類似結(jié)構(gòu)體的嵌套。

以上就是Golang中interface的基本用法詳解的詳細內(nèi)容,更多關(guān)于Golang interface的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Golang中的sync.WaitGroup用法實例

    Golang中的sync.WaitGroup用法實例

    這篇文章主要介紹了Golang中的sync.WaitGroup用法實例,WaitGroup的用途,它能夠一直等到所有的goroutine執(zhí)行完成,并且阻塞主線程的執(zhí)行,直到所有的goroutine執(zhí)行完成,需要的朋友可以參考下
    2015-07-07
  • 一文詳解在Go中如何使用Viper來管理配置

    一文詳解在Go中如何使用Viper來管理配置

    Viper 是一個功能齊全的 Go 應(yīng)用程序配置庫,支持很多場景。在本文中,我們將深入探討 Viper 的各種用法和使用場景,以幫助讀者更好地了解和使用 Viper 來管理應(yīng)用程序配置,感興趣的同學(xué)可以參考閱讀
    2023-05-05
  • 一文詳解Golang中的匿名變量

    一文詳解Golang中的匿名變量

    匿名變量是一種特殊類型的變量,可以簡化代碼并提高可讀性,本文將為大家詳細介紹一下golang中匿名變量的定義、特性和使用方法,需要的可以參考下
    2023-09-09
  • go語言切片slice使用細節(jié)和注意事項整理大全

    go語言切片slice使用細節(jié)和注意事項整理大全

    這篇文章主要給大家介紹了關(guān)于go語言切片slice使用細節(jié)和注意事項整理的相關(guān)資料,需要的朋友可以參考下
    2024-05-05
  • Go語言怎么使用變長參數(shù)函數(shù)

    Go語言怎么使用變長參數(shù)函數(shù)

    本文主要介紹了Go語言怎么使用變長參數(shù)函數(shù),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • go語言編程二維碼生成及識別

    go語言編程二維碼生成及識別

    這篇文章主要為大家介紹了go語言編程二維碼的生成及識別示例演示,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-04-04
  • 深入了解Go語言中web框架的中間件運行機制

    深入了解Go語言中web框架的中間件運行機制

    大家在使用iris框架搭建web系統(tǒng)時,一定會用到中間件。那么你了解中間件的運行機制嗎?你知道為什么在iris和gin框架的請求處理函數(shù)中要加c.Next()函數(shù)嗎?本文就和大家一起探究該問題的答案
    2023-02-02
  • golang?基于?mysql?簡單實現(xiàn)分布式讀寫鎖

    golang?基于?mysql?簡單實現(xiàn)分布式讀寫鎖

    這篇文章主要介紹了golang?基于mysql簡單實現(xiàn)分布式讀寫鎖,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-09-09
  • golang進行簡單權(quán)限認證的實現(xiàn)

    golang進行簡單權(quán)限認證的實現(xiàn)

    本文主要介紹了golang簡單權(quán)限認證的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • golang 格式化輸入輸出操作

    golang 格式化輸入輸出操作

    這篇文章主要介紹了golang 格式化輸入輸出操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12

最新評論