搜尋
首頁後端開發Golanggolang的介面有啥用

golang的介面有啥用

Jan 04, 2023 pm 08:42 PM
golang介面go語言

在golang中,介面是一種類型,是用來將對方法進行一個收束,其作用是:1、作為方法的收束器,進行物件導向設計;2、作為各種資料的承載者,可以用來接收函數參數等。介面的定義語法「type 介面類型名稱interface{方法名( 參數清單1 ) 傳回值清單}」;當方法名稱首字母是大寫且這個介面類型名稱首字母也是大寫時,這個方法可以被介面所在的套件(package)以外的程式碼存取。

golang的介面有啥用

本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。

一、介面(interface)是什麼

interface是一組method簽章的組合,我們透過interface定義物件的一組行為。

(注意method 和普通func的區別)

Interface是一種類型,和往常語言的介面不一樣,它只是用來將對方法進行一個收束。然而正是這種收束,使GO語言擁有了基於功能的物件導向。

介面的主要功能:

1.作為方法的收束器,進行物件導向設計。

2.作為各種資料的承載者,可以用來接收函數參數等。

這也是,GO語言提倡面向介面程式設計

二、介面的定義使用

2.1定義

類似結構體

type 接口类型名 interface{
    方法名1( 参数列表1 ) 返回值列表1
    方法名2( 参数列表2 ) 返回值列表2
    …
}

當然這只是有方法的介面定義,面向資料的介面不用。

  • 介面名稱:使用type將介面定義為自訂的類型名稱。 Go語言的介面在命名時,通常會在單字後面加上er,如有寫入運算的介面叫Writer,有字串功能的介面叫Stringer等。介面名最好要能突顯該介面的型別意義。

  • 方法名稱:當方法名稱首字母是大寫且這個介面類型名稱首字母也是大寫時,這個方法可以被介面所在的套件(package)以外的程式碼存取。

  • 參數清單、傳回值清單:參數清單和傳回值清單中的參數變數名稱可以省略

2.2使用

一個物件只要全部實作了介面中的方法,那就實作了這個介面。換句話說,介面就是一個需要實作的方法清單

//定义接口
type FastfoodStore interface{
    MakeHamberger()
    MakeFriedChips()
    MakeSoftDrink()
}
//定义结构体
type KFC struct{}
type HambergerKing struct{}

//实现了接口中所有的方法
func (kfc KFC) MakeHamberger(){
    fmt.println("肯德基的汉堡")
}
func (kfc KFC) MakeFriedChips(){
    fmt.println("肯德基的薯条")
}
func (kfc KFC) MakeSoftDrink(){
    fmt.println("肯德基的饮料")
}

func (K *HambergerKing) MakeHameberger(){
    fmt.println("汉堡王的汉堡")
}
func (K *HambergerKing) MakeFriedChips(){
    fmt.println("汉堡王的薯条")
}
func (K *HambergerKing) MakeSoftDrink(){
    fmt.println("汉堡王的饮料")
}

我們可以看到不同於Java的介面明確實現,Go的語言是隱式實現的。

  • 在Java 中:實作介面需要明確地宣告介面並實作所有方法;
  • 在Go 中:實作介面的所有方法都隱含地實作了介面;

那麼GO語言是如何檢查該類型是否是介面呢?

答案:Go 語言只會在傳遞參數、傳回參數、變數賦值時才會對某個型別是否實作介面進行檢查。從類型檢查的過程來看,編譯器僅在需要時才檢查類型,類型實作介面時才需要實作介面中的全部方法,不需要像 Java 等程式語言一樣明確宣告。

我們可以看到在上面實作介面的時候,KFC是用結構體物件實現的,而Hamberger king是透過指標實現的兩者有什麼不同呢?

答案:差別在於我們初始化介面的時候

//结构体初始化和指针初始化
var f faststore = KFC{}             //可以通过编译
var f faststore = &KFC{}            //可以通过编译

var f faststore = HambergerKing{}    //无法通过编译
var f faststore = &HambergerKing{}    //可以通过编译

所以在我們使用指標進行實現,結構體初始化時,為啥不行呢?

答:Go 語言在傳遞參數時都是傳值的。

golang的介面有啥用

如上圖所示,無論上述程式碼中初始化的變數指標或結構體,使用 呼叫方法時都會發生值拷貝:

如上图左侧,对于 &HambergerKing{} 来说,这意味着拷贝一个新的 &HambergerKing{} 指针,这个指针与原来的指针指向一个相同并且唯一的结构体,所以编译器可以隐式的对变量解引用(dereference)获取指针指向的结构体;
如上图右侧,对于 HambergerKing{} 来说,这意味着方法会接受一个全新的 HambergerKing{},因为方法的参数是*HambergerKing,编译器不会无中生有创建一个新的指针;即使编译器可以创建新指针,这个指针指向的也不是最初调用该方法的结构体;
上面的分析解释了指针类型的现象,当我们使用指针实现接口时,只有指针类型的变量才会实现该接口;当我们使用结构体实现接口时,指针类型和结构体类型都会实现该接口。当然这并不意味着我们应该一律使用结构体实现接口,这个问题在实际工程中也没那么重要,在这里我们只想解释现象背后的原因。

在上面我们说过,interface有两种用法,现在介绍了其中一种就是作为方法的收束器。那么第二种就是作为数据的承载者

2.3 数据承载者

作为数据容器时,接口就是一个“空”接口,这个空来形容没有Method。空interface(interface{})不包含任何的method,正因为如此,所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method),但是空interface在我们需要存储任意类型的数值的时候相当有用,因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。

需要注意的是,与 C 语言中的 void * 不同,interface{} 类型不是任意类型。如果我们将类型转换成了 interface{} 类型,变量在运行期间的类型也会发生变化,获取变量类型时会得到 interface{}。

我们尝试从底层实现来解释两种用法的不同,你会好理解一些。Go 语言使用 runtime.iface 表示第一种接口,使用 runtime.eface 表示第二种不包含任何方法的接口 interface{},两种接口虽然都使用 interface 声明,但是由于后者在 Go 语言中很常见,所以在实现时使用了特殊的类型。

golang的介面有啥用

空接口作为函数的参数

使用空接口实现可以接收任意类型的函数参数。

// 空接口作为函数参数
func show(a interface{}) {
    fmt.Printf("type:%T value:%v\n", a, a)
}

空接口作为map的值

使用空接口实现可以保存任意值的字典。

// 空接口作为map值
    var studentInfo = make(map[string]interface{})
    studentInfo["name"] = "Wilen"
    studentInfo["age"] = 18
    studentInfo["married"] = false
    fmt.Println(studentInfo)
//gin框架的gin.H{}

三、关于接口类型转换

interface 可以存储所有的值,那么自然会涉及到类型转换这个话题。与此同时,我们也将在这节细说类型转换中,因为结构体实现和结构体指针实现的接口的异同。

3.1结构体指针实现接口

//我们仍然运用上面快餐店的例子
type Store interface{
    MakeHamberger()
}
type KFC struct{
    name string
}
func (k *KFC) MakeHamberger(){
    fmt.println(k.name+"制作了一个汉堡")
}
func main(){
    var s store = &KFC{name:"东街店"}
    store.MakeHamberger()
}

这里将上述代码生成的汇编指令拆分成三部分分析:

1.结构体 KFC 的初始化;

KFC的初始化又可以分为下面几步:

  • 获取 KFC 结构体类型指针并将其作为参数放到栈上;

  • 通过 CALL 指定调用 runtime.newobject函数,这个函数会以 KFC 结构体类型指针作为入参,分配一片新的内存空间并将指向这片内存空间的指针返回到 SP+8 上;

  • SP+8 现在存储了一个指向 KFC 结构体的指针,我们将栈上的指针拷贝到寄存器 DI 上方便操作;

  • 由于 Cat 中只包含一个字符串类型的 Name 变量,所以在这里会分别将字符串地址 &"东街店" 和字符串长度 6 设置到结构体上。

golang的介面有啥用

2.赋值触发的类型转换过程;

因为 KFC 结构体的定义中只包含一个字符串,而字符串在 Go 语言中总共占 16 字节,所以每一个 KFC 结构体的大小都是 16 字节。初始化 KFC 结构体之后就进入了将 *KFC 转换成 Store 类型的过程了:

类型转换的过程比较简单,Store 作为一个包含方法的接口,它在底层使用 [runtime.iface] 结构体表示。runtime.iface 结构体包含两个字段,其中一个是指向数据的指针,另一个是表示接口和结构体关系的 tab 字段,我们已经通过上一段代码 SP+8 初始化了 KFC 结构体指针,这段代码只是将编译期间生成的 runtime.itab 结构体指针复制到 SP 上:

golang的介面有啥用

到这里,我们会发现 SP ~ SP+16 共同组成了 runtime.iface 结构体。

3.调用接口的方法 Quack();

栈上的这个 runtime.iface 也是 MakeHamberger() 方法的第一个入参。通过CALL()完成方法的调用。

3.2 结构体实现接口

//我们仍然运用上面快餐店的例子
type Store interface{
    MakeHamberger()
}
type KFC struct{
    name string
}
func (k KFC) MakeHamberger(){
    fmt.println(k.name+"制作了一个汉堡")
}
func main(){
    var s store = KFC{name:"东街店"}
    store.MakeHamberger()
}

如果我们在初始化变量时使用指针类型 &KFC{Name: "东街店"} 也能够通过编译,不过生成的汇编代码和上一节中的几乎完全相同,所以这里也就不分析这个情况了。

初始化 KFC 结构体;

在栈上初始化 KFC 结构体,而上一节的代码在堆上申请了 16 字节的内存空间,栈上只有一个指向 KFC 的指针。

完成从 KFC 到 Store 接口的类型转换;

初始化结构体后会进入类型转换的阶段,编译器会将 go.itab."".KFC,"".Store 的地址和指向 KFC 结构体的指针作为参数一并传入 runtime.convT2I 函数:这个函数会获取 runtime.itab 中存储的类型,根据类型的大小申请一片内存空间并将 elem 指针中的内容拷贝到目标的内存中:

func convT2I(tab *itab, elem unsafe.Pointer) (i iface) {
    t := tab._type
    x := mallocgc(t.size, t, true)
    typedmemmove(t, x, elem)
    i.tab = tab
    i.data = x
    return
}

runtime.convT2I 会返回一个 runtime.iface,其中包含 runtime.itab 指针和 KFC 变量。当前函数返回之后,main 函数的栈上会包含以下数据:

golang的介面有啥用

SP 和 SP+8 中存储的 runtime.itab 和 KFC 指针是 runtime.convT2I 函数的入参,这个函数的返回值位于 SP+16,是一个占 16 字节内存空间的 runtime.iface 结构体,SP+32 存储的是在栈上的 KFC 结构体,它会在 runtime.convT2I 执行的过程中拷贝到堆上。

3.3类型断言

如何将一个接口类型转换成具体类型?

x.(T)

非空接口

func main() {
    var c Store = &KFC{Name: "东街店"}
    switch c.(type) {
    case *KFC:
        kfc := c.(*KFC)
        kfc.MakeHamberger()
    }
}

因为 Go 语言的编译器做了一些优化,所以代码中没有runtime.iface 的构建过程,不过对于这一节要介绍的类型断言和转换没有太多的影响。

switch语句生成的汇编指令会将目标类型的 hash 与接口变量中的 itab.hash 进行比较

空接口

func main() {
    var c interface{} = &KFC{Name: "东街店"}
    switch c.(type) {
    case *KFC:
        kfc := c.(*KFC)
        kfc.MakeHamberger()
    }
}

上述代码会在类型断言时就不是直接获取变量中具体类型的 runtime._type,而是从 eface._type 中获取,汇编指令仍然会使用目标类型的 hash 与变量的类型比较.

【相关推荐:Go视频教程编程教学

以上是golang的介面有啥用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
在Golang和Python之間進行選擇:適合您的項目在Golang和Python之間進行選擇:適合您的項目Apr 19, 2025 am 12:21 AM

golangisidealforperformance-Critical-clitageAppations and ConcurrentPrompromming,而毛皮刺激性,快速播種和可及性。 1)forhigh-porformanceneeds,pelectgolangduetoitsefefsefefseffifeficefsefeflicefsiveficefsiveandconcurrencyfeatures.2)fordataa-fordataa-fordata-fordata-driventriventriventriventriventrivendissp pynonnononesp

Golang:並發和行動績效Golang:並發和行動績效Apr 19, 2025 am 12:20 AM

Golang通過goroutine和channel實現高效並發:1.goroutine是輕量級線程,使用go關鍵字啟動;2.channel用於goroutine間安全通信,避免競態條件;3.使用示例展示了基本和高級用法;4.常見錯誤包括死鎖和數據競爭,可用gorun-race檢測;5.性能優化建議減少channel使用,合理設置goroutine數量,使用sync.Pool管理內存。

Golang vs. Python:您應該學到哪種語言?Golang vs. Python:您應該學到哪種語言?Apr 19, 2025 am 12:20 AM

Golang更適合系統編程和高並發應用,Python更適合數據科學和快速開發。 1)Golang由Google開發,靜態類型,強調簡潔性和高效性,適合高並發場景。 2)Python由GuidovanRossum創造,動態類型,語法簡潔,應用廣泛,適合初學者和數據處理。

Golang vs. Python:性能和可伸縮性Golang vs. Python:性能和可伸縮性Apr 19, 2025 am 12:18 AM

Golang在性能和可擴展性方面優於Python。 1)Golang的編譯型特性和高效並發模型使其在高並發場景下表現出色。 2)Python作為解釋型語言,執行速度較慢,但通過工具如Cython可優化性能。

Golang vs.其他語言:比較Golang vs.其他語言:比較Apr 19, 2025 am 12:11 AM

Go語言在並發編程、性能、學習曲線等方面有獨特優勢:1.並發編程通過goroutine和channel實現,輕量高效。 2.編譯速度快,運行性能接近C語言。 3.語法簡潔,學習曲線平緩,生態系統豐富。

Golang和Python:了解差異Golang和Python:了解差異Apr 18, 2025 am 12:21 AM

Golang和Python的主要區別在於並發模型、類型系統、性能和執行速度。 1.Golang使用CSP模型,適用於高並發任務;Python依賴多線程和GIL,適合I/O密集型任務。 2.Golang是靜態類型,Python是動態類型。 3.Golang編譯型語言執行速度快,Python解釋型語言開發速度快。

Golang vs.C:評估速度差Golang vs.C:評估速度差Apr 18, 2025 am 12:20 AM

Golang通常比C 慢,但Golang在並發編程和開發效率上更具優勢:1)Golang的垃圾回收和並發模型使其在高並發場景下表現出色;2)C 通過手動內存管理和硬件優化獲得更高性能,但開發複雜度較高。

Golang:雲計算和DevOps的關鍵語言Golang:雲計算和DevOps的關鍵語言Apr 18, 2025 am 12:18 AM

Golang在雲計算和DevOps中的應用廣泛,其優勢在於簡單性、高效性和並發編程能力。 1)在雲計算中,Golang通過goroutine和channel機制高效處理並發請求。 2)在DevOps中,Golang的快速編譯和跨平台特性使其成為自動化工具的首選。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱工具

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具