【Golang - 90天從新手到大師】Day14 - 方法和接口

一.? go方法

go方法:在函數的func和函數名間增加一個特殊的接收器類型,接收器可以是結構體類型或非結構體類型。接收器可以在方法內部訪問。創建一個接收器類型為Type的methodName方法。

func (t Type) methodName(parameter list) {}

go引入方法的原因:

1)go不是純粹的面向對象編程語言,而且Go不支持類。因此,基于類型的方法是一種實現和類相似行為的途徑。

2)相同名字的方法可以定義在不同的類型上,而相同名字的函數不被允許。

方法調用

t.methodName(parameter list)

指針接收器與值接收器

區別:指針接收器的方法內部的改變對外可見,而值接收器不會改變方法外部的變量。

對于指針接收器&T Type而言,(&T).methodName與T.methodName等價。

匿名字段的方法

屬于結構體的匿名字段的方法可以被直接調用,就好像這些方法是屬于定義了匿名字段的結構體一樣。

在方法中使用值接收器 與 在函數中使用值參數

當一個函數有一個值參數,它只能接受一個值參數。

當一個方法有一個值接收器,它可以接受值接收器和指針接收器。

package?main
import "fmt"
type rectangle struct {        length int        width int}
func area(r rectangle){        fmt.Printf("Area Function result: %d\n", (r.length * r.width))}
func (r rectangle)area(){        fmt.Printf("Area method result: %d\n", (r.length * r.width))}
func main(){        r := rectangle{                length: 10,                width: 5,        }area(r)        r.area()p := &r//      area(p) // cannot use p (type *rectangle) as type rectangle in argument to area        p.area() //通過指針調用接收器}

在方法中使用指針接收器 與 在函數中使用指針參數

函數使用指針參數只接受指針,而使用指針接收器的方法可以使用值接收器和指針接收器。

在非結構體上的方法

為了在一個類型上定義一個方法,方法的接收器類型定義和方法的定義應該在同一個包中。

對于內建類型,如int,應該在文件中創建一個類型別名,然后創建一個以該類型別名為接收器的方法。

二.? go接口

接口是方法(方法簽名,method signature)的集合。當一個類型定義了接口中的所有方法,就稱它實現了該接口。與OOP類似,接口定義了一個類型應該具有的方法,由該類型決定如何實現這些方法。

type myInterface interface{    method1()    method2()}

接口調用

永遠不要使用一個指針指向一個接口類型,因為它已經是一個指針。 函數參數為interface{}時可以接收任何類型參數,即使是指針類型,也應該是interface{},而不是*interface{}。

//interface definitiontype VowelsFinder interface {      FindVowels() []rune}
type MyString string
//MyString implements VowelsFinderfunc (ms MyString) FindVowels() []rune {      var vowels []rune    for _, rune := range ms {        if rune == 'a' || rune == 'e' || rune == 'i' || rune == 'o' || rune == 'u' {            vowels = append(vowels, rune)        }    }    return vowels}name := MyString("Sam Anderson")    var v VowelsFinder    v = name // possible since MyString implements VowelsFinder    fmt.Printf("Vowels are %c", v.FindVowels())

如果一個類型包含了接口中聲明的所有方法,那么它就隱式地實現了 Go 接口

接口的內部表示

可以把接口看作內部的一個元組?(type, value)。?type?是接口底層的具體類型(Concrete Type),而?value?是具體類型的值。

圖片圖片

(接口值是一個兩個字長度的數據結構,第一個字包含一個指向內部表的指針,內部表iTable存儲值類型信息以及方法集。第二個字是一個指向所存儲值的指針。)

一個接口的值,接口值,由兩個部分組成,一個具體的類型和那個類型的值。它們被稱為接口的動態類型和動態值。

對于Go語言這種靜態類型的語言,類型是編譯期的概念:因此一個類型不是一個值。

從概念上講,不論接口值多大,動態值總是可以容下它。

接口值可以使用和!=來進行比較。兩個接口值相等僅當他們是nil值或者它們的動態類型相同并且動態值也根據這個動態類型的操作相等。因為接口值是可比較的,所以它們可以用在map的鍵或者作為switch語句的操作數。

然而,如果兩個接口值的動態類型相同,但是這個動態類型是不可比較的(比如切片),將它們比較就會失敗并且panic。

var x interface{} = []int{1, 2, 3}fmt.Println(x == x) // panic: comparing uncomparable
 type []int

考慮到這點,接口類型是非常與眾不同的。其它類型要么是安全的可比較類型(如基本類型和指針)要么是完全不可比較的類型(如切片,映射類型,和函數),但是在比較接口值或者包含了接口值的聚合類型時,我們必須要意識到潛在的panic。同樣的風險也存在于使用接口作為map的鍵或者switch的操作數。只能比較你非常確定它們的動態值是可比較類型的接口值。

type Test interface {      Tester()}
type MyFloat float64
func (m MyFloat) Tester() {      fmt.Println(m)}
func describe(t Test) {      fmt.Printf("Interface type %T value %v\n", t, t)}
func main() {      var t Test    f := MyFloat(89.7)    t = f    describe(t)    t.Tester()}
輸出:Interface type main.myFloat value 89.789.7

空接口

沒有包含方法的接口稱為空接口。空接口表示為?interface{}。由于空接口沒有方法,因此所有類型都實現了空接口。

當指定參數為空接口時,可以接收任意類型,那如何獲取參數的值呢?? 通過類型斷言。v, ok := p.(int),判定參數是否為int并獲取參數值。

函數可以接收interface{}作為參數,但最好不要返回interface{}。

類型斷言

類型斷言用于提取接口的底層值(Underlying Value)。

在語法 i.(T)?中,接口 i 的具體類型是 T,該語法用于獲得接口的底層值。

v, ok := i.(T)

如果 i 的具體類型是 T,那么 v 賦值為 i 的底層值,而 ok 賦值為 true。

如果?i?的具體類型不是?T,那么?ok?賦值為?false,v?賦值為?T?類型的零值,此時程序不會報錯

類型選擇(Type Switch)

類型選擇用于將接口的具體類型與很多 case 語句所指定的類型進行比較。它與一般的 switch 語句類似。

類型選擇的語法是 i.(type),獲取接口的類型,type是固定關鍵字。需要注意的是,只有接口類型才可以使用類型選擇。

還可以將一個類型和接口相比較。如果一個類型實現了接口,那么該類型與其實現的接口就可以互相比較。

type Describer interface {        Describe()}
type Person struct {        name string        age int}func (p Person) Describe(){        fmt.Printf("%s is %d years old\n", p.name, p.age)}
func findType(i interface{}){        switch v := i.(type){                case Describer:                        v.Describe()                default:                        fmt.Printf("unknown type\n")        }}
func main(){        findType("wang")        p := Person{                name: "qing",                age: 25,        }findType(p)}

在上面程序中,結構體?Person?實現了?Describer?接口。在第 19 行的 case 語句中,v?與接口類型?Describer?進行了比較。p?實現了?Describer,因此滿足了該 case 語句,于是當程序運行到第 32 行的?findType(p)?時,程序調用了?Describe()?方法。

package main
import "fmt"
type InterfaceA interface {    Print()    Get() string}
type InterfaceB interface {    Get() string    Print()}
type InterfaceC interface {    Print()}
type People struct {    name string}
func (p *People) Print() {    fmt.Println(p.name)}
func (p *People) Get() string {    return p.name}
// 只要兩個接口擁有相同的方法列表(次序不同不要緊),那么他們就是等價的,可以相互賦值
func main() {    var a InterfaceA    var b InterfaceB    var c InterfaceC    a = b    p := &People{name: "wang"}    a = p //接口賦值若錯誤,編譯時直接報錯, a = *p    b = a    a.Print()    b.Print()    v, ok := b.(*People) // 接口查詢若錯誤,編譯直接報錯, b.(People)    fmt.Println(v, ok)    c = a     // 多方法接口可以賦值到少方法接口    c.Print()    //    b = c    // InterfaceC does not implement InterfaceB(missing Get method)    //  b.Print()}

實現接口:指針接受者與值接受者

使用值接受者聲明的方法,接口既可以用值來調用,也能用指針調用。

對于使用指針接受者的方法,接口必須用一個指針或者一個可取得地址的值(&method)來調用。但接口中存儲的具體值(Concrete Value)并不能取到地址,對于編譯器無法自動獲取 a 的地址,于是程序報錯。

type Describer interface {      Describe()}type Person struct {      name string    age  int}
func (p Person) Describe() { // 使用值接受者實現      fmt.Printf("%s is %d years old\n", p.name, p.age)}
type Address struct {    state   string    country string}
func (a *Address) Describe() { // 使用指針接受者實現    fmt.Printf("State %s Country %s", a.state, a.country)}
func main() {      var d1 Describer    p1 := Person{"Sam", 25}    d1 = p1    d1.Describe()    p2 := Person{"James", 32}    d1 = &p2    d1.Describe()var d2 Describer    a := Address{"Washington", "USA"}/* 如果下面一行取消注釋會導致編譯錯誤:       cannot use a (type Address) as type Describer       in assignment: Address does not implement       Describer (Describe method has pointer       receiver)    */    //d2 = ad2 = &a // 這是合法的    // 因為在第 22 行,Address 類型的指針實現了 Describer 接口    d2.Describe()}

接口的嵌套

type SalaryCalculator interface {      DisplaySalary()}
type LeaveCalculator interface {      CalculateLeavesLeft() int}
type EmployeeOperations interface {      SalaryCalculator    LeaveCalculator}

接口的零值

接口的零值是 nil。對于值為 nil 的接口,其底層值(Underlying Value)和具體類型(Concrete Type)都為 nil。對于值為 nil 的接口,由于沒有底層值和具體類型,當我們試圖調用它的方法時,程序會產生 panic 異常。

一個接口值基于它的動態類型被描述為空或非空,可以通過w==nil或w!=nil來判斷接口值是否為空。

當且僅當動態值和動態類型都為 nil 時,接口類型值才為 nil。

w=nil將重置接口w的所有部分(類型和值)為nil。

警告:一個包含nil指針的接口可能不是nil接口。

var buf *bytes.Bufferf(buf)
func f(out io.Writer){    // ...    if out != nil {        out.Write([]byte("done!\n"))    // painic: nil pointer dereference    }}

當執行f(buf)時,f函數的out參數賦了一個bytes.Buffer的空指針,所以out的動態值是nil,但是它的動態類型是bytes.Buffer,意思是out變量是一個包含空指針的非空接口,所以out!=nil的結果依然是true。

正確的處理是 var buf io.Writer,因此可以避免一開始就將一個不完全的值賦值給這個接口。

Go接口最佳實踐

1)傾向于使用小的接口定義,很多接口只包含一個方法。? ? 如Reader,Writer,便于類型實現接口,方法太多,類型實現越麻煩。

2)較大的接口定義,可以由多個小接口定義組合而成。? 即接口的嵌套。

3)只依賴于必要功能的最小接口。方法或函數的接口參數的范圍或方法越小越好,這樣便于參數的調用,和方法或函數被其他程序調用。

如func StoreData(reader Reader) error{},能傳遞Reader就不傳遞ReadWriter。

4)接口一般有默認實現,應用時嵌套默認實現。

package main
import "fmt"
type Animal interface {        Speak()        SpeakTo(string)}
// default implementationtype Pet struct {}
func (p *Pet) Speak() {        fmt.Print("Pet...")}
func (p *Pet) SpeakTo(host string){        p.Speak()        fmt.Println("---", host)}
// real functiontype Dog struct {        *Pet  // include default struct}/*func (d *Dog) Speak() {        fmt.Print("Dog")}*/
func (d *Dog) SpeakTo(host string){        d.Speak() // no implement, using default        fmt.Println(host)}
func main(){        dog := new(Dog)        dog.SpeakTo("Chao")}

歡迎加入Go語言的學習wx群:wdw11079533

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/37841.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/37841.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/37841.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

在 MATLAB 中顯示 3D 圖像

文章目錄 前言1. 曲面圖 (Surface Plot)2. 網格圖 (Mesh Plot)3. 散點圖 (Scatter Plot)4. 等值線圖 (Contour Plot) 前言 提示:這里可以添加本文要記錄的大概內容: 項目需要: 提示:以下是本篇文章正文內容,下面案例…

享元模式(設計模式)

享元模式(Flyweight Pattern)是一種結構型設計模式,它通過共享細粒度對象來減少內存使用,從而提高性能。在享元模式中,多個對象可以共享相同的狀態以減少內存消耗,特別適合用于大量相似對象的場景。 享元模…

解決“Duplicate keys detected: ‘ ‘.This may cause an update error.”問題

問題原因 出現“Duplicate keys detected”的錯誤,通常表示在v-for指令中使的:key綁定值有重復。 如果前端是靜態數據,一般能自我避免:key綁定值有重復。如果前端是綁定的動態數據,那么需要另外提供一個唯一的鍵。 在這個例子中&#xff0c…

【LeetCode】接雨水

目錄 一、題目二、解法完整代碼 一、題目 給定 n 個非負整數表示每個寬度為 1 的柱子的高度圖,計算按此排列的柱子,下雨之后能接多少雨水。 示例 1: 輸入:height [0,1,0,2,1,0,1,3,2,1,2,1] 輸出:6 解釋&#xff…

面向對象,常用類,集合,異常,JDBC,mysql數據庫內容的復習,

1,面向對象 面向對象與面向過程對比 面向過程:關注過程,適合解決簡單直接的問題,代碼結構以函數為單位,如C語言。 面向對象:關注類,適合解決復雜問題更加適合解決復雜的項目中的問題等等&…

跨平臺編程:在Conda中搭建R語言環境的終極指南

🌐 跨平臺編程:在Conda中搭建R語言環境的終極指南 🌐 在數據科學和統計分析領域,R語言以其強大的數據處理能力和豐富的圖形表示功能而廣受歡迎。然而,對于習慣了使用Linux操作系統的用戶來說,如何方便地在…

【UML用戶指南】-23-對高級行為建模-狀態機

目錄 1、概述 2、狀態 2.1、狀態的組成 3、轉移 3.1、轉移的組成 4、高級狀態和轉移 4.1、進入效應和退出效應 4.2、內部轉移 4.3、do活動 4.4、延遲事件 4.5、子狀態機 5、子狀態 5.1、非正交子狀態 5.2、歷史狀態 5.3、正交子狀態 6、分叉與匯合 7、主動對象…

GOROOT GOPATH GOPROXY GO111MODULE

GOROOT GOROOT代表Go的安裝目錄。可執行程序go(或go.exe)和gofmt(或gofmt.exe)位于 GOROOT/bin目錄中。 配置GOROOT環境變量,其值為Go的安裝目錄;然后在環境變量PATH中添加GOROOT/bin路徑。 注意:GOROOT變量只是代表了安裝目錄,不…

泛型的實際應用示例

泛型的實際應用示例 1. 集合框架中的泛型 在Java的集合框架中,泛型被廣泛使用以確保類型安全并減少運行時錯誤。以下是一個使用泛型ArrayList的示例: java import java.util.ArrayList; import java.util.List; public class GenericCollectionsExamp…

【面試題】信息系統安全運維要做什么

信息系統安全運維是確保信息系統穩定、可靠、安全運行的一系列活動和措施。 其主要包括以下幾個方面: 1.系統監控: 實時監測信息系統的運行狀態,如服務器的性能指標、網絡流量、應用程序的運行情況等。通過監控工具,及時發現系統…

企業數據治理的下一步是數據資產管理?

隨著信息技術的飛速發展和數字化轉型的深入推進,企業數據已成為驅動業務增長和創新的核心要素。當企業數據治理工作取得顯著成效后,如何進一步發揮數據的價值,實現數據資產的有效管理,成為企業面臨的重要課題。 數據治理的基石作用…

算法練習——函數、遞歸和遞推

在此記錄一些有關函數、遞歸和遞推的問題。所有題目均來自洛谷的題單能力提升綜合題單Part1 入門階段 - 題單 - 洛谷 | 計算機科學教育新生態 (luogu.com.cn) (實際上都沒有用遞推做) [NOIP2001 普及組] 數的計算 題目描述 給出正整數 n n n&#xf…

學習感悟丨在譽天學習數通HCIP怎么樣

大家好,我是譽天學員的徐同學,學習的數通HCIP課程。 在學校的時候,聽說下半年就要出去實習了,心中坎坷不安,現在我學到的知識遠遠不夠的。然后就想著學點東西充實一下自己的知識面和專業能力,有一次和同學談…

【漏洞復現】飛企互聯——SQL注入

聲明:本文檔或演示材料僅供教育和教學目的使用,任何個人或組織使用本文檔中的信息進行非法活動,均與本文檔的作者或發布者無關。 文章目錄 漏洞描述漏洞復現測試工具 漏洞描述 飛企互聯-FE企業運營管理平臺是一個基于云計算、智能化、大數據…

[圖解] 向量數據庫之何謂乘積量化器?

Product Quantization 在前面一節講解了向量數據庫索引相關的內容,那么本節將會講解其中壓縮方法的量化手段:乘積量化器。 簡單來說將向量的所有維度劃分為多個子空間,每個子空間一部分維度,然后每個子空間獨立去找最近距離。例如…

haproxy實現代理和負載均衡

HaProxy介紹: haproxy是法國開發者威利塔羅在2000年使用C語言開發的一個開源軟件,是一款具備高并發(一萬以上)、高性能的TCP和HTTP負載均衡器,支持基于cookie的持久性,自動故障切換,支持正則表達式及web狀態統計&…

Numpy array和Pytorch tensor的區別

1.Numpy array和Pytorch tensor的區別 筆記來源: 1.Comparison between Pytorch Tensor and Numpy Array 2.numpy.array 4.Tensors for Neural Networks, Clearly Explained!!! 5.What is a Tensor in Machine Learning? 1.1 Numpy Array Numpy array can only h…

arthas監控工具筆記(一)

文章目錄 啟動 math-game啟動 arthas查看 dashboard通過 thread 命令來獲取到math-game進程的 Main Class通過 jad 來反編譯 Main Class退出 arthas 界面linux服務器掛不上進程怎么辦? 核心表達式變量loader 本次調用類所在的 ClassLoaderclazz 本次調用類的 Class 引用method…

信息學奧賽初賽天天練-39-CSP-J2021基礎題-哈夫曼樹、哈夫曼編碼、貪心算法、滿二叉樹、完全二叉樹、前中后綴表達式轉換

PDF文檔公眾號回復關鍵字:20240629 2022 CSP-J 選擇題 單項選擇題(共15題,每題2分,共計30分:每題有且僅有一個正確選項) 5.對于入棧順序為a,b,c,d,e的序列,下列( )不合法的出棧序列 A. a,b&a…

螺旋矩陣問題C代碼

給定一個n行m列的二維數組,要求按順時針螺旋順序輸出矩陣中的所有元素,n和m小于等于10 如下圖是一個三行四列的螺旋矩陣 要求輸出 1 2 3 4 8 12 11 10 9 5 6 7 全局變量定義 int a[11][11]; int vis[11][11]; // 訪問標記數組關鍵代碼如下 int dx[] …