1. 程式人生 > >Go語言基礎:Interface

Go語言基礎:Interface

我們知道 Golang 中沒有 class 的概念,而是通過 interface 型別轉換支援在動態型別語言中常見的 鴨子型別 達到執行時多型的效果。

官方文件 中對 Interface 是這樣定義的:

An interface type specifies a method set called its interface. A variable of interface type can store a value of any type with a method set that is any superset of the interface. Such a type is said to implement the interface. The value of an uninitialized variable of interface type is nil.

一個 interface 型別定義了一個 方法集 作為其 介面。 interface 型別的變數可以儲存含有屬於這個 interface 型別方法集超集的任何型別的值,這時我們就說這個型別 實現 了這個 介面。未被初始化的 interface 型別變數的零值為 nil。

對於 interface 型別的方法集來說,其中每一個方法都必須有一個不重複並且不是 補位名(即單下劃線 _)的方法名。

拿 官方文件的例子 來說:

如果我們定義 File 介面型別:

// A simple File interface
type File interface {
    Read(b Buffer
) bool Write(b Buffer) bool Close() }

那麼,假設有兩個其他型別 S1 和 S2 都擁有下面的方法集:

func (p T) Read(b Buffer) bool { return … }
func (p T) Write(b Buffer) bool { return … }
func (p T) Close() { … }

注意,上面程式碼中的 T 代表 S1 或者 S2。這時,無論 S1 和 S2 各自的方法集中還有哪些方法,我們都說 S1 和 S2 各自 實現 了介面 File。

對於空介面 interface {} 來說,由於其不含有任何方法簽名,在 Golang 語言中我們可以認為所有型別都 實現 了空介面。

一個型別可以 實現 一個或者多個介面,假設我們還有下面的介面型別:

type Locker interface {
    Lock()
    Unlock()
}

而 S1 和 S2 各自的方法集中都含有下面的方法簽名:

func (p T) Lock() { … }
func (p T) Unlock() { … }

同樣的,上面程式碼中的 T 代表 S1 或者 S2,這時我們稱 S1 和 S2 各自都 實現 了介面 File 和 Locker。

一個介面型別(假設為 T)可以 嵌入 另一個介面型別(假設為 E),這被稱為 embedding interface E in T,而且,所有 E 中的方法都會被新增到 T 中。需要注意的是,一個介面不能 嵌入 其本身,也不能 嵌入 任何已經 嵌入 其本身的其他介面,這樣會造成遞迴 嵌入。

還是通過下面的例子來看看:

package main

import "fmt"

// 定義介面型別 PeopleGetter 包含獲取基本資訊的方法
type PeopleGetter interface {
    GetName() string
    GetAge() int
}

// 定義介面型別 EmployeeGetter 包含獲取薪水的方法
// EmployeeGetter 介面中嵌入了 PeopleGetter 介面,前者將獲取後者的所有方法
type EmployeeGetter interface {
    PeopleGetter
    GetSalary() int
    Help()
}

// 定義結構 Employee
type Employee struct {
    name   string
    age    int
    salary int
    gender string
}

// 定義結構 Employee 的方法
func (self *Employee) GetName() string {
    return self.name
}

func (self *Employee) GetAge() int {
    return self.age
}

func (self *Employee) GetSalary() int {
    return self.salary
}

func (self *Employee) Help() {
    fmt.Println("This is help info.")
}

// 匿名介面可以被用作變數或者結構屬性型別
type Man struct {
    gender interface {
        GetGender() string
    }
}

func (self *Employee) GetGender() string {
    return self.gender
}

// 定義執行回撥函式的介面
type Callbacker interface {
    Execute()
}

// 定義函式型別 func() 的新型別 CallbackFunc
type CallbackFunc func()

// 實現 CallbackFunc 的 Execute() 方法
func (self CallbackFunc) Execute() { self() }

func main() {
    // 空介面的使用,空介面型別的變數可以儲存任何型別的值
    // 空格口型別的變數非常類似於弱型別語言中的變數
    var varEmptyInterface interface{}
    fmt.Printf("varEmptyInterface is of type %T\n", varEmptyInterface)
    varEmptyInterface = 100
    fmt.Printf("varEmptyInterface is of type %T\n", varEmptyInterface)
    varEmptyInterface = "Golang"
    fmt.Printf("varEmptyInterface is of type %T\n", varEmptyInterface)

    // Employee 實現了 PeopleGetter 和 EmployeeGetter 兩個介面
    varEmployee := Employee{
        name:   "Jack Ma",
        age:    50,
        salary: 100000000,
        gender: "Male",
    }
    fmt.Println("varEmployee is: ", varEmployee)
    varEmployee.Help()
    fmt.Println("varEmployee.name = ", varEmployee.GetName())
    fmt.Println("varEmployee.age = ", varEmployee.GetAge())
    fmt.Println("varEmployee.salary = ", varEmployee.GetSalary())

    // 匿名介面物件的使用
    varMan := Man{&Employee{
        name:   "Nobody",
        age:    20,
        salary: 10000,
        gender: "Unknown",
    }}
    fmt.Println("The gender of Nobody is: ", varMan.gender.GetGender())

    // 介面型別轉換,從超集到子集的轉換是可以的
    // 從方法集的子集到超集的轉換會導致編譯錯誤
    // 這種情況下 switch 不支援 fallthrough
    var varEmpInter EmployeeGetter = &varEmployee
    switch varEmpInter.(type) {
    case nil:
        fmt.Println("nil")
    case PeopleGetter:
        fmt.Println("PeopleGetter")
    default:
        fmt.Println("Unknown")
    }

    // 使用 “執行回撥函式的介面物件” 執行回撥函式
    // 這種做法的優勢是函式顯式地 “實現” 特定介面
    varCallbacker := CallbackFunc(func() { println("I am a callback function.") })
    varCallbacker.Execute()

}

將上面的程式碼存入原始檔 interface.go 並使用 go run interface.go 可以看到下面的輸入:

varEmptyInterface is of type <nil>
varEmptyInterface is of type int
varEmptyInterface is of type string
varEmployee is:  {Jack Ma 50 100000000 Male}
This is help info.
varEmployee.name =  Jack Ma
varEmployee.age =  50
varEmployee.salary =  100000000
The gender of Nobody is:  Unknown
PeopleGetter

I am a callback function.

關於 interface 更多需要注意的地方還有:

介面型別不包含任何資料成員。
介面型別進行轉換的時候,預設返回的是臨時物件,所以如果需要對原始物件進行修改,需要獲取原始物件的指標。

相關推薦

Go語言基礎Interface

我們知道 Golang 中沒有 class 的概念,而是通過 interface 型別轉換支援在動態型別語言中常見的 鴨子型別 達到執行時多型的效果。 官方文件 中對 Interface 是這樣定義的: An interface type specifi

Go語言基礎array、slice、make和new操作、map

array 陣列宣告和賦值 go的陣列宣告跟C語言很相似,除了陣列型別放在變數名後面【這點跟變數的宣告一樣】。 陣列的定義格式: //一維陣列 var 陣列名[n] 陣列型別 //需要注意的是:'[n]'可以寫成'[...]',go會自動根據元素個數來計

Go語言基礎深入理解 struct 內嵌 inteface

1. 如何更好的理解匿名介面 // 匿名介面可以被用作變數或者結構屬性型別 type Man struct { gender interface { GetGender()

Go語言【第十四篇】Go語言基礎總結

cal pro 結果 第十四 深入 得到 divider math XP Go語言類型轉換 類型轉換用於將一種數據類型的變量轉換為另外一種類型的變量,Go語言類型轉換基本格式如下: type_name(expression) type_name為類型,expression為

go語言基礎語法面向物件程式設計

一、匿名組合 1.匿名欄位初始化 type Person struct { name string sex byte age int } type Student struct { Person//只有型別,沒有名字,匿名欄位,繼承了Person裡面的成員

go語言基礎語法異常處理,文字檔案處理,JSON處理,檔案操作

一、異常處理 1.err介面的使用 err1 := fmt.Errorf("%s", "this is normal error") fmt.Println("err1=", err1) err2 := errors.New("this is normal error,

帶你學夠浪Go語言基礎系列 - 8分鐘學基礎語法

> 文章每週持續更新,原創不易,「三連」讓更多人看到是對我最大的肯定。可以微信搜尋公眾號「 後端技術學堂 」第一時間閱讀(一般比部落格早更新一到兩篇) 對於一般的語言使用者來說 ,20% 的語言特性就能夠滿足 80% 的使用需求,剩下在使用中掌握。 基於這一理論,Go 基礎系列的文章不會刻意追求面面俱到

帶你學夠浪Go語言基礎系列-環境配置和 Hello world

> 文章每週持續更新,原創不易,「三連」讓更多人看到是對我最大的肯定。可以微信搜尋公眾號「 後端技術學堂 」第一時間閱讀(一般比部落格早更新一到兩篇) 前面幾周陸陸續續寫了一些後端技術的文章,包括資料庫、微服務、記憶體管理等等,我比較傾向於成體系的學習,所以資料庫和微服務還有後續系列文章補充。 最近工作上

帶你學夠浪Go語言基礎系列 - 8分鐘學控制流語句

★ 文章每週持續更新,原創不易,「三連」讓更多人看到是對我最大的肯定。可以微信搜尋公眾號「 後端技術學堂 」第一時間閱讀(一般比部落格早更新一到兩篇) ” 對於一般的語言使用者來說 ,20% 的語言特性就能夠滿足 80% 的使用需求,剩下在使用中掌握。基於這一理論,Go 基礎系列的文章不會刻意追求面面俱到,

帶你學夠浪Go語言基礎系列 - 8分鐘學複合型別

★ 文章每週持續更新,原創不易,「三連」讓更多人看到是對我最大的肯定。可以微信搜尋公眾號「 後端技術學堂 」第一時間閱讀(一般比部落格早更新一到兩篇) ” 對於一般的語言使用者來說 ,20% 的語言特性就能夠滿足 80% 的使用需求,剩下在使用中掌握。基於這一理論,Go 基礎系列的文章不會刻意追求面面俱到,

C語言基礎遞歸函數,全局(局)變量

否則 fib 語言 factorial 必須 不起作用 聲明 遞歸函數 tor #include <stdio.h>int factorial(int a); int Fibonacci(a);long Hanoi(a); void main(){ } 函

Go語言基礎單元測試示例

== stat 示例 語言 單元 ould true def 函數 這個要熟悉原理,要能寫。。 但現在。。。。。 註意,沒有main函數,以_test.go結尾,命令go test -v package main import ( "testing"

java語言基礎go語言基礎的區別

clas pan 搭建 pat classpath spa jdk java strong 一:開發環境搭建 1、java語言的開發環境搭建 我的電腦, 屬性,高級設置,環境變量 (1)在系統變量點新建 變量名:JAVA_HOME,變量值:把JDK的根目錄復制放在這 (2

GO語言method、interface、reflection、select

語言 go method 方法method -GO方法雖沒有class,依舊有method -通過顯示說明receiver來實現與某個類型的組合 -只能為同一個包中的類型定義方法 -RECEIVER可以是類型的值或指針 -不存在方法重載 -可以使用值或指針來調用方法,編譯器會自動完成轉換 -從某種

Go語言備忘錄基本數據結構

備忘錄 結構體 參考 映射 pac 語言 學習筆記 spa 常量 本文內容是本人對Go語言的變量、常量、數組、切片、映射、結構體的備忘錄,記錄了關鍵的相關知識點,以供翻查。 文中如有錯誤的地方請大家指出,以免誤導!轉摘本文也請註明出處:Go語言備忘錄:基本數據結構,多謝!

go 語言 基礎 類型(1)

必須 const 表達式 基本 基本類 image 關鍵字 字符串 函數 變量 使用關鍵字 var定義變量,自動初始化為0值。如果提供初始化值,可省略變量類型,由編譯器自動推斷。 在函數內部可以使用 := 方式定義變量 func main() { x := 123

go語言練習文件哈希

lse int \n code test c4c open value face package main import ( "crypto/sha256" "encoding/hex" "fmt" "io" "os" ) func gethash(path

Go語言基礎介紹

unit plain inter mage popu 函數返回 int cto 可選 Go是一個開源的編程語言。Go語言被設計成一門應用於搭載Web服務器,存儲集群或類似用途的巨型中央服務器的系統編程語言。目前,Go最新發布版本為1.10. Go語言可以運

Go語言開發(二)、Go語言基礎

Go 語言 基礎 Go語言開發(二)、Go語言基礎 一、Go語言程序結構 Go語言程序基本結構如下:A、包聲明B、引入包C、函數D、變量E、語句 & 表達式F、註釋 package main //包聲明 import "fmt" //引入包 func main(){ //main函數

和IDEA一樣好用的go語言IDEGoland

alt 下載 免費 dea 遺憾 rain 分享圖片 版本 最新 因為IDEA和Goland來自同一家非常有名的捷克公司:JetBrains 很好用,智能化程度高 最新版下載地址:http://www.jetbrains.com/go/?fromMenu 相關使用手冊:ht