簡體   English   中英

滿足接口的 Go 結構體方法的類型

[英]Types of Go struct methods that satisfy an interface

給出以下 Go 代碼示例:

package main

import "fmt"

type greeter interface {
    hello()
    goodbye()
}

type tourGuide struct {
    name string
}

func (t tourGuide) hello() {
    fmt.Println("Hello", t.name)
}

func (t *tourGuide) goodbye() {
    fmt.Println("Goodbye", t.name)
}

func main() {
    var t1 tourGuide = tourGuide{"James"}
    t1.hello()   // Hello James
    t1.goodbye() // Goodbye James (same as (&t1).goodbye())

    var t2 *tourGuide = &tourGuide{"Smith"}
    t2.hello()   // Hello Smith
    t2.goodbye() // Goodbye Smith (same as (*t2).hello())

    // illegal: t1 is not assignable to g1 (why?)
    // var g1 greeter = t1

    var g2 greeter = t2
    g2.hello()   // Hello Smith
    g2.goodbye() // Goodbye Smith
}

我可以使用類型為 tourGuide t1的變量或指向 tourGuide t2的指針來調用結構體tourGuide的兩種方法。 換句話說,我可以使用T*T類型的變量調用帶有T接收器的方法。 類似地,我可以調用一個方法*T接收機使用類型的變量T (如果T可尋址的)或*T 我知道編譯器處理這里的差異(請參閱我在代碼中的注釋)。

但是,當我們實現接口時,情況會發生變化。 在上面的代碼中,一個類型為greeter接口的變量可以從一個指向tourGuide的指針tourGuide但不能從一個tourGuide

誰能告訴我為什么會這樣? 為什么我可以調用t1.hello()t1.goodbye()但不知何故t1對接口greeter是不夠的?

如果一個方法有一個指針接收器,那么只有一個指針值可以用作接收器值。 因此,要對某個值調用此方法,該值本身必須是一個指針,或者必須可以獲取其地址(用作接收器)。

例如,如果您有一個變量,它是可尋址的,因此可以獲取其地址並將其用作接收器。 規范允許你這樣做,這會自動發生。

封裝在接口中的值不可尋址。 創建接口值時,將復制包裝在接口中的值。 因此無法獲取其地址。 從理論上講,您可以允許獲取副本的地址,但這將是(甚至)比它提供的好處更多的混亂的根源,因為地址將指向副本,並且具有指針接收器的方法只能修改副本而不是原版。

請參閱此答案,其中詳細說明/證明在創建接口值時復制了值: 切片如何包含自身?

如果您有一個指向結構的指針,那么 go 將允許您訪問結構上的屬性及其具有值類型接收器(與指針接收器相關)的函數,而無需取消引用您的指針,但這僅適用於一級指針,請參閱下面的代碼,其中我將 t2 轉換為指向tourguide的指針,此時我需要明確取消引用它以使其重新成為指向tourguide的指針。 將指向結構的指針的第一級視為一種特殊情況,它允許您使用語法糖來訪問值類型的屬性和函數,從而不必經常手動取消對變量的引用。

package main

import "fmt"

type greeter interface {
    hello()
    goodbye()
}

type tourGuide struct {
    name string
}

func (t tourGuide) hello() {
    fmt.Println("Hello", t.name)
}

func (t *tourGuide) goodbye() {
    fmt.Println("Goodbye", t.name)
}

func main() {
    var t1 tourGuide = tourGuide{"James"}
    t1.hello()   // Hello James
    t1.goodbye() // Goodbye James (same as (&t1).goodbye())

    var tmpT2 *tourGuide = &tourGuide{"Smith"}
    var t2 **tourGuide = &tmpT2
    (*t2).hello()   // Hello Smith
    (*t2).goodbye() // Goodbye Smith (same as (*t2).hello())

    //illegal: t1 is not assignable to g1 (why?)
    //var g1 greeter = t1

    //now this is illegal too
    //var g2 greeter = t2

    var g3 greeter = (*t2)
    g3.hello()   // Hello Smith
    g3.goodbye() // Goodbye Smith
}

我在這里的回答解釋了為什么 Go 會阻止您獲取存儲在接口中的值的地址。

TL;博士其因為一個指向其中,當不同類型B的值隨后被存儲在接口指向的接口的類型A的值將被無效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM