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

Go語(yǔ)言中interface語(yǔ)法與使用詳解

 更新時(shí)間:2022年07月14日 14:41:36   作者:yuqiang870  
Go語(yǔ)言里面設(shè)計(jì)最精妙的應(yīng)該算interface,它讓面向?qū)ο?內(nèi)容組織實(shí)現(xiàn)非常的方便,下面這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言中interface語(yǔ)法與使用的相關(guān)資料,需要的朋友可以參考下

初識(shí)interface

Go語(yǔ)言的面向?qū)ο蟮闹R(shí)點(diǎn)時(shí),發(fā)現(xiàn)它的面向?qū)ο竽芰θ?interface 撐著,而且它的 interface 還與我們以前知道的 interface 完全不同。故而整個(gè)過程不斷的思考為什么要如此設(shè)計(jì)?這樣設(shè)計(jì)給我們帶來了什么影響?

interface(接口)是golang最重要的特性之一,實(shí)現(xiàn)多態(tài)。Interface類型可以定義一組方法,但是這些不需要實(shí)現(xiàn)。并且interface不能包含任何變量。

基本語(yǔ)法

定義一個(gè)接口

type Person interface {
    // 聲明方法
    method1(參數(shù)列表)返回值列表
    method2(參數(shù)列表)返回值列表
}

實(shí)現(xiàn)一個(gè)接口

func (t 自定義類型)method1(參數(shù)列表)返回值列表 {
    //方法實(shí)現(xiàn)
}
func (t 自定義類型)method2(參數(shù)列表)返回值列表 {
    //方法實(shí)現(xiàn)
}

小結(jié):

(1)接口里的所有方法都沒有方法體,即接口的方法都是沒有實(shí)現(xiàn)的方法。接口體現(xiàn)了程序設(shè)計(jì)的多態(tài)和高內(nèi)聚低耦合的思想。

(2)Go中的接口,不需要顯示的實(shí)現(xiàn)。只要一個(gè)變量,含有接口類型中的所有方法,那么這個(gè)變量就實(shí)現(xiàn)這個(gè)接口。因此,Go中沒有implement關(guān)鍵字樣。

(3)Go實(shí)現(xiàn)接口與方法有關(guān),與接口本身叫什么名字沒有特別大的關(guān)系。變量需要實(shí)現(xiàn)接口所有的方法。

其他注意事項(xiàng)

(1)接口本身不能創(chuàng)建實(shí)例,但是可以指向一個(gè)實(shí)現(xiàn)了該接口的自定義類型的變量(實(shí)例)。

package main

import "fmt"

// Person 定義接口
type Person interface {
	GetName() string
	GetAge() uint32
}

// Student 定義類型
type Student struct {
	Name string
	Age uint32
}

func (s Student) GetName()  string{
	return s.Name
}
func (s Student) GetAge()  uint32{
	return s.Age
}

func main() {

var student Student
	student.Age = 12
	student.Name = "小明"

var person Person
	person = student  //接口執(zhí)行向student
	fmt.Printf("name:%s,age: %d\n", person.GetName(), person.GetAge())
}

(2)接口中所有的方法都沒有方法體,即都是沒有實(shí)現(xiàn)的方法。

(3)在Go中,一個(gè)自定義類型需要將某個(gè)接口的所有方法都實(shí)現(xiàn),我們說這個(gè)自定義類型實(shí)現(xiàn)了該接口。

(4)一個(gè)自定義類型只有實(shí)現(xiàn)了某個(gè)接口,才能將該自定義類型的實(shí)例(變量)賦給接口類型。

(5)只要是自定義數(shù)據(jù)類型就可以實(shí)現(xiàn)接口,不僅僅是結(jié)構(gòu)體類型。

(6)一個(gè)自定義類型可以實(shí)現(xiàn)多個(gè)接口。

(7)Go接口不能有任何變量。

(8)一個(gè)接口可以繼承多個(gè)別的接口,這時(shí)如果要實(shí)現(xiàn)這個(gè)接口必須實(shí)現(xiàn)它繼承的所有接口的方法。在低版本的Go編輯器中,一個(gè)接口繼承其他多個(gè)接口時(shí),不允許繼承的接口有相同的方法名。比如A接口繼承B、C接口,B、C接口的方法名不能一樣。高版本的Go編輯器沒有相關(guān)問題。

(9)interface類型默認(rèn)是一個(gè)指針(引用類型),如果沒有對(duì)interface初始化就使用,那么會(huì)輸出nil。

(10)空接口interface{}沒有任何方法,所以所有類型都實(shí)現(xiàn)了空接口,即我們可以把任何一個(gè)變量賦給空接口類型。

interface底層實(shí)現(xiàn)

Go的interface源碼在Golang源碼的runtime目錄中。

Go的interface是由兩種類型來實(shí)現(xiàn)的:iface和eface。

iface

iface是包含方法的interface,如:

type Person interface {
	Print()
}

iface的源代碼是:

type iface struct {
	tab  *itab
	data unsafe.Pointer
}

iface具體結(jié)構(gòu)是:

itab是iface不同于eface的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)。其包含兩部分:一部分是唯一確定包含該interface的具體結(jié)構(gòu)類型,一部分是指向具體方法集的指針。其具體結(jié)構(gòu)為:

itab結(jié)構(gòu)

屬性 itab的源代碼是:

type itab struct {
	inter *interfacetype //此屬性用于定位到具體interface
	_type *_type         //此屬性用于定位到具體interface
	hash  uint32         // copy of _type.hash. Used for type switches.
	_     [4]byte
	fun   [1]uintptr     // variable sized. fun[0]==0 means _type does not implement inter.
}

屬性interfacetype類似于_type,其作用就是interface的公共描述,類似的還有maptype、arraytype、chantype…其都是各個(gè)結(jié)構(gòu)的公共描述,可以理解為一種外在的表現(xiàn)信息。interfacetype源碼如下:

type interfacetype struct {
	typ     _type
	pkgpath name
	mhdr    []imethod
}
type imethod struct {
	name nameOff
	ityp typeOff
}

iface的整體結(jié)構(gòu)為:

我們來看一個(gè)例子,對(duì)于含有方法的interface賦值后的內(nèi)部結(jié)構(gòu)是怎樣的呢?

package main

import "fmt"

// Person 定義接口
type Person interface {
	GetName() string
	GetAge() uint32
}

// Student 定義類型
type Student struct {
	Name string
	Age uint32
}

func (s Student) GetName()  string{
	return s.Name
}
func (s Student) GetAge()  uint32{
	return s.Age
}

func main() {

var student Student
	student.Age = 12
	student.Name = "小明"

var person Person
	person = student
	fmt.Printf("name:%s,age: %d\n", person.GetName(), person.GetAge())
}

運(yùn)行結(jié)果:

name:小明,age: 12

Process finished with the exit code 0

內(nèi)存分布示意圖:

eface

eface是不包含方法的interface,即空interface,如:

type Person interface {
}

或者

var person interface{} = xxxx實(shí)體

侵入式與非侵入式的理解

侵入式

你的代碼里已經(jīng)嵌入了別的代碼,這些代碼可能是你引入過的框架,也可能是你通過接口繼承得來的,比如:java中的繼承,必須顯示的表明我要繼承那個(gè)接口,這樣你就可以擁有侵入代碼的一些功能。所以我們就稱這段代碼是侵入式代碼。

優(yōu)點(diǎn):通過侵入代碼與你的代碼結(jié)合可以更好的利用侵入代碼提供給的功能。

缺點(diǎn):框架外代碼就不能使用了,不利于代碼復(fù)用。依賴太多重構(gòu)代碼太痛苦了。

非侵入式

正好與侵入式相反,你的代碼沒有引入別的包或框架,完完全全是自主開發(fā)。比如go中的接口,不需要顯示的繼承接口,只需要實(shí)現(xiàn)接口的所有方法就叫實(shí)現(xiàn)了該接口,即便該接口刪掉了,也不會(huì)影響我,所有g(shù)o語(yǔ)言的接口數(shù)非侵入式接口;再如Python所崇尚的鴨子類型。

優(yōu)點(diǎn):代碼可復(fù)用,方便移植。非侵入式也體現(xiàn)了代碼的設(shè)計(jì)原則:高內(nèi)聚,低耦合。

缺點(diǎn):無法復(fù)用框架提供的代碼和功能。

接下來看看java與go語(yǔ)言編程實(shí)現(xiàn)接口來理解侵入式與非侵入式的區(qū)別。

java語(yǔ)言實(shí)現(xiàn)

定義接口

public interface IPersonService {
    String getName();
    Integer getAge();
}

實(shí)現(xiàn)接口的類

public class PersonService implements IPersonService{
    @Override
    public String getName() {
        return "小明";
    }
    @Override
    public Integer getAge() {
        return 12;
    }
}

go語(yǔ)言實(shí)現(xiàn)

package main
import "fmt"

// Person 定義接口
type Person interface {
	GetName() string
	GetAge() uint32
}
// Student 定義類型
type Student struct {
	Name string
	Age uint32
}

func (s Student) GetName()  string{
	return s.Name
}
func (s Student) GetAge()  uint32{
	return s.Age
}

func main() {

var student Student
	student.Age = 12
	student.Name = "小明"

var person Person
	person = student
	fmt.Printf("name:%s,age: %d\n", person.GetName(), person.GetAge())
}

通過上面的例子我們總結(jié)了以下問題:

  1. 侵入式通過 implements 把實(shí)現(xiàn)類與具體接口綁定起來了,因此有了強(qiáng)耦合;
  2. 假如修改了接口方法,則實(shí)現(xiàn)類方法必須改動(dòng);
  3. 假如類想再實(shí)現(xiàn)一個(gè)接口,實(shí)現(xiàn)類也必須進(jìn)行改動(dòng);
  4. 后續(xù)實(shí)現(xiàn)此接口的類,必須了解相關(guān)的接口;
    Go語(yǔ)言非侵入式的方式很好地解決了這幾個(gè)問題,只要實(shí)現(xiàn)了實(shí)現(xiàn)了與接口相同的方法,就實(shí)現(xiàn)了這個(gè)接口。隨著代碼量的增加,根本不需要的關(guān)心實(shí)現(xiàn)了哪些接口,不需要刻意去先定義接口再實(shí)現(xiàn)接口的固定模式,在原有類新增實(shí)現(xiàn)接口時(shí),不需要更改類,做到低侵入式、低耦合開發(fā)的好處。

interface的應(yīng)用場(chǎng)景

類型轉(zhuǎn)換

類型推斷可將接口變量還原為原始類型,或用來判斷是否實(shí)現(xiàn)了某個(gè)更具體的接口類型。

type data int
  
func(d data)String()string{ 
   return fmt.Sprintf("data:%d",d) 
} 
  
func main() { 
   var d data=15
   var x interface{} =d
  
   if n,ok:=x.(fmt.Stringer);ok{  // 轉(zhuǎn)換為更具體的接口類型 
       fmt.Println(n) 
    } 
  
   if d2,ok:=x.(data);ok{        // 轉(zhuǎn)換回原始類型 
       fmt.Println(d2) 
    } 
  
   e:=x.(error)           // 錯(cuò)誤:main.data is not error
   fmt.Println(e) 
}

輸出為:

data:15
data:15
panic:interface conversion:main.data is not error:missing method Error

但是此處會(huì)觸發(fā)panic,使用ok-idiom模式,即便轉(zhuǎn)換失敗也不會(huì)引發(fā)panic。還可用switch語(yǔ)句在多種類型間做出推斷匹配,這樣空接口就有更多發(fā)揮空間。

func main() {
var x interface{} =func(x int)string{ 
       return fmt.Sprintf("d:%d",x) 
    } 
  
   switch v:=x.(type) {            // 局部變量v是類型轉(zhuǎn)換后的結(jié)果 
   case nil: 
       println("nil") 
   case*int: 
       println(*v) 
   case func(int)string: 
       println(v(100)) 
   case fmt.Stringer: 
       fmt.Println(v) 
   default: 
       println("unknown") 
    } 
}

輸出為:

d:100

實(shí)現(xiàn)多態(tài)功能

多態(tài)功能是interface實(shí)現(xiàn)的重要功能,也是Golang中的一大行為特色,其多態(tài)功能一般要結(jié)合Go method實(shí)現(xiàn),作為函數(shù)參數(shù)可以容易的實(shí)現(xiàn)多臺(tái)功能。

package main

import "fmt"

// notifier是一個(gè)定義了通知類行為的接口
type notifier interface {
  notify()
}

// 定義user及user.notify方法
type user struct {
  name string
  email string
}

func (u *user) notify() {
  fmt.Printf("Sending user email to %s<%s>\n",
    u.name,
    u.email)
}

// 定義admin及admin.notify方法
type admin struct {
  name string
  email string
}

func (a *admin) notify() {
  fmt.Printf("Sending admin email to %s<%s>\n",
    a.name,
    a.email)
}

func main() {
  // 創(chuàng)建一個(gè)user值并傳給sendNotification
  bill := user{"Bill", "bill@email.com"}
  sendNotification(&bill)

  // 創(chuàng)建一個(gè)admin值并傳給sendNotification
  lisa := admin{"Lisa", "lisa@email.com"}
  sendNotification(&lisa)
}

// sendNotification接受一個(gè)實(shí)現(xiàn)了notifier接口的值
// 并發(fā)送通知
func sendNotification(n notifier) {
  n.notify()
}

上述代碼中實(shí)現(xiàn)了一個(gè)多態(tài)的例子,函數(shù)sendNotification接受一個(gè)實(shí)現(xiàn)了notifier接口的值作為參數(shù)。既然任意一個(gè)實(shí)體類型都能實(shí)現(xiàn)該接口,那么這個(gè)函數(shù)可以針對(duì)任意實(shí)體類型的值來執(zhí)行notify方法,調(diào)用notify時(shí),會(huì)根據(jù)對(duì)象的實(shí)際定義來實(shí)現(xiàn)不同的行為,從而實(shí)現(xiàn)多態(tài)行為。

補(bǔ)充:interface 與 nil 的比較

引用公司內(nèi)部同事的討論議題,覺得之前自己也沒有理解明白,為此,單獨(dú)羅列出來,例子是最好的說明,如下

package main
 
import (
    "fmt"
    "reflect"
)
 
type State struct{}
 
func testnil1(a, b interface{}) bool {
    return a == b
}
 
func testnil2(a *State, b interface{}) bool {
    return a == b
}
 
func testnil3(a interface{}) bool {
    return a == nil
}
 
func testnil4(a *State) bool {
    return a == nil
}
 
func testnil5(a interface{}) bool {
    v := reflect.ValueOf(a)
    return !v.IsValid() || v.IsNil()
}
 
func main() {
    var a *State
    fmt.Println(testnil1(a, nil))
    fmt.Println(testnil2(a, nil))
    fmt.Println(testnil3(a))
    fmt.Println(testnil4(a))
    fmt.Println(testnil5(a))
}

返回結(jié)果如下

false
false
false
true
true

為啥呢?

一個(gè)interface{}類型的變量包含了2個(gè)指針,一個(gè)指針指向值的類型,另外一個(gè)指針指向?qū)嶋H的值 對(duì)一個(gè)interface{}類型的nil變量來說,它的兩個(gè)指針都是0;但是var a *State傳進(jìn)去后,指向的類型的指針不為0了,因?yàn)橛蓄愋土耍?所以比較為false。 interface 類型比較, 要是 兩個(gè)指針都相等, 才能相等。

總結(jié)

到此這篇關(guān)于Go語(yǔ)言中interface語(yǔ)法與使用詳解的文章就介紹到這了,更多相關(guān)Go語(yǔ)言 interface詳解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang上下文Context的常見應(yīng)用場(chǎng)景

    Golang上下文Context的常見應(yīng)用場(chǎng)景

    Golang?context主要用于定義超時(shí)取消,取消后續(xù)操作,在不同操作中傳遞值。本文通過簡(jiǎn)單易懂的示例進(jìn)行說明,感興趣的可以了解一下
    2023-04-04
  • 解決Golang小數(shù)float64在實(shí)際工程中加減乘除的精度問題

    解決Golang小數(shù)float64在實(shí)際工程中加減乘除的精度問題

    這篇文章主要介紹了解決Golang小數(shù)float64在實(shí)際工程中加減乘除的精度問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03
  • 一文帶你了解Golang中interface的設(shè)計(jì)與實(shí)現(xiàn)

    一文帶你了解Golang中interface的設(shè)計(jì)與實(shí)現(xiàn)

    本文就來詳細(xì)說說為什么說?接口本質(zhì)是一種自定義類型,以及這種自定義類型是如何構(gòu)建起?go?的?interface?系統(tǒng)的,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-01-01
  • 詳解Go語(yǔ)言中自定義結(jié)構(gòu)體能作為map的key嗎

    詳解Go語(yǔ)言中自定義結(jié)構(gòu)體能作為map的key嗎

    在Go中,引用類型具有動(dòng)態(tài)的特性,可能會(huì)被修改或指向新的數(shù)據(jù),這就引發(fā)了一個(gè)問題—能否將包含引用類型的自定義結(jié)構(gòu)體作為map的鍵呢,本文就來和大家想想講講
    2023-06-06
  • 詳解Go?flag實(shí)現(xiàn)二級(jí)子命令的方法

    詳解Go?flag實(shí)現(xiàn)二級(jí)子命令的方法

    這篇文章主要介紹了Go?flag?詳解,實(shí)現(xiàn)二級(jí)子命令,本文就探討一下?Go?語(yǔ)言中如何寫一個(gè)擁有類似特性的命令行程序,需要的朋友可以參考下
    2022-07-07
  • Golang你一定要懂的連接池實(shí)現(xiàn)

    Golang你一定要懂的連接池實(shí)現(xiàn)

    這篇文章主要介紹了Golang你一定要懂的連接池實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Mac OS系統(tǒng)安裝golang教程

    Mac OS系統(tǒng)安裝golang教程

    這篇文章主要介紹了Mac OS系統(tǒng)安裝golang教程,本文還同時(shí)介紹了Sublime Text開發(fā)工具的配置,需要的朋友可以參考下
    2015-01-01
  • golang利用redis和gin實(shí)現(xiàn)保存登錄狀態(tài)校驗(yàn)登錄功能

    golang利用redis和gin實(shí)現(xiàn)保存登錄狀態(tài)校驗(yàn)登錄功能

    這篇文章主要介紹了golang利用redis和gin實(shí)現(xiàn)保存登錄狀態(tài)校驗(yàn)登錄功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-01-01
  • 解析Go 標(biāo)準(zhǔn)庫(kù) http.FileServer 實(shí)現(xiàn)靜態(tài)文件服務(wù)

    解析Go 標(biāo)準(zhǔn)庫(kù) http.FileServer 實(shí)現(xiàn)靜態(tài)文件服務(wù)

    http.FileServer 方法屬于標(biāo)準(zhǔn)庫(kù) net/http,返回一個(gè)使用 FileSystem 接口 root 提供文件訪問服務(wù)的 HTTP 處理器。下面通過本文給大家介紹Go 標(biāo)準(zhǔn)庫(kù) http.FileServer 實(shí)現(xiàn)靜態(tài)文件服務(wù)的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2018-08-08
  • golang Goroutine超時(shí)控制的實(shí)現(xiàn)

    golang Goroutine超時(shí)控制的實(shí)現(xiàn)

    日常開發(fā)中我們大概率會(huì)遇到超時(shí)控制的場(chǎng)景,比如一個(gè)批量耗時(shí)任務(wù)、網(wǎng)絡(luò)請(qǐng)求等,本文主要介紹了golang Goroutine超時(shí)控制的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-09-09

最新評(píng)論