溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Golang怎么自定義類型和方法集

發(fā)布時(shí)間:2023-02-24 16:56:52 來(lái)源:億速云 閱讀:114 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹“Golang怎么自定義類型和方法集”,在日常操作中,相信很多人在Golang怎么自定義類型和方法集問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Golang怎么自定義類型和方法集”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

我們先以一個(gè)謎題開(kāi)頭練練手:

package main
 
import (
    "encoding/json"
    "fmt"
    "time"
)
 
type MyTime time.Time
 
func main() {
    myTime := MyTime(time.Now()) // 假設(shè)獲得的時(shí)間是 2022年7月20日20:30:00,時(shí)區(qū)UTC+8
    res, err := json.Marshal(myTime)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(res))
}

請(qǐng)問(wèn)上述代碼會(huì)輸出什么:

1.編譯錯(cuò)誤

2.運(yùn)行時(shí)panic

3.{}

4."2022-07-20T20:30:00.135693011+08:00"

很多人一定會(huì)選4吧,然而答案是3:

$ go run customize.go
 
{}

是不是很意外,MyTime就是time.Time,理論上應(yīng)該也實(shí)現(xiàn)了json.Marshaler,為什么輸出的是空的呢?

實(shí)際上這是最近某個(gè)群友遇到的問(wèn)題,乍一看像是golang的bug,但其實(shí)還是沒(méi)掌握語(yǔ)言的基本規(guī)則。

在深入下去之前,我們先問(wèn)自己兩個(gè)問(wèn)題:

  • MyTime 真的是 Time 類型嗎?

  • MyTime 真的實(shí)現(xiàn)了 json.Marshaler 嗎?

對(duì)于問(wèn)題1,只需要引用spec里的說(shuō)明即可:

A named type is always different from any other type.

https://go.dev/ref/spec#Type_identity

意思是說(shuō),只要是type定義出來(lái)的類型,都是不同的(type alias除外),即使他們的underlying type是一樣的,也是兩個(gè)不同的類型。

那么問(wèn)題1的答案就知道了,顯然MyTime不是time.Time。

既然MyTime不是Time,那它是否能用Time類型的method呢?畢竟MyTime的基底類型是Time呀。我們寫(xiě)段代碼驗(yàn)證下:

package main
 
import (
    "fmt"
    "time"
)
 
type MyTime time.Time
 
func main() {
    myTime := MyTime(time.Now()) // 假設(shè)獲得的時(shí)間是 2022年7月20日20:30:00,時(shí)區(qū)UTC+8
    res, err := myTime.MarsharlJSON()
    if err != nil {
            panic(err)
    }
    fmt.Println(string(res))
}

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

# command-line-arguments
./checkoutit.go:12:24: myTime.MarsharlJSON undefined (type MyTime has no field or method MarsharlJSON)

現(xiàn)在問(wèn)題2也有答案了:MyTime沒(méi)有實(shí)現(xiàn)json.Marshaler。

那么對(duì)于一個(gè)沒(méi)有實(shí)現(xiàn)json.Marshaler的類型,json是怎么序列化的呢?這里就不賣(mài)關(guān)子了,文檔里有寫(xiě),對(duì)于沒(méi)實(shí)現(xiàn)Marshaler的類型,默認(rèn)的流程使用反射獲取所有非export的字段,然后依次序列化,我們?cè)倏纯磘ime的結(jié)構(gòu):

type Time struct {
        // wall and ext encode the wall time seconds, wall time nanoseconds,
        // and optional monotonic clock reading in nanoseconds.
        //
        // From high to low bit position, wall encodes a 1-bit flag (hasMonotonic),
        // a 33-bit seconds field, and a 30-bit wall time nanoseconds field.
        // The nanoseconds field is in the range [0, 999999999].
        // If the hasMonotonic bit is 0, then the 33-bit field must be zero
        // and the full signed 64-bit wall seconds since Jan 1 year 1 is stored in ext.
        // If the hasMonotonic bit is 1, then the 33-bit field holds a 33-bit
        // unsigned wall seconds since Jan 1 year 1885, and ext holds a
        // signed 64-bit monotonic clock reading, nanoseconds since process start.
        wall uint64
        ext  int64
 
        // loc specifies the Location that should be used to
        // determine the minute, hour, month, day, and year
        // that correspond to this Time.
        // The nil location means UTC.
        // All UTC times are represented with loc==nil, never loc==&utcLoc.
        loc *Location
}

里面都是非公開(kāi)字段,所以直接序列化后整個(gè)結(jié)果就是{}。當(dāng)然,Time類型自己重新實(shí)現(xiàn)了json.Marshaler,所以可以正常序列化成我們期望的值。

而我們的MyTime沒(méi)有實(shí)現(xiàn)整個(gè)接口,所以走了默認(rèn)的序列化流程。

所以我們可以得出一個(gè)重要的結(jié)論:從某個(gè)類型A派生出的類型B,B并不能獲得A的方法集中的任何一個(gè)。

想要B擁有A的所有方法也不是不行,但得和type B A這樣的形式說(shuō)再見(jiàn)了。

方法一是使用type alias:

- type MyTime time.Time
+ type MyTime = time.Time
 
func main() {
-   myTime := MyTime(time.Now()) // 假設(shè)獲得的時(shí)間是 2022年7月20日20:30:00,時(shí)區(qū)UTC+8
+   var myTime MyTime = time.Now() // 假設(shè)獲得的時(shí)間是 2022年7月20日20:30:00,時(shí)區(qū)UTC+8
    res, err := json.Marshal(myTime)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(res))
}

類型別名自如其名,就是創(chuàng)建了一個(gè)類型A的別名而沒(méi)有定義任何新類型(注意那兩行改動(dòng))。現(xiàn)在MyTime就是Time了,自然也可以直接利用Time的MarshalJSON。

方法二,使用內(nèi)嵌類型:

- type MyTime time.Time
+ type MyTime struct {
+     time.Time
+ }
 
func main() {
-   myTime := MyTime(time.Now()) // 假設(shè)獲得的時(shí)間是 2022年7月20日20:30:00,時(shí)區(qū)UTC+8
+   myTime := MyTime{time.Now}
    res, err := myTime.MarsharlJSON()
    if err != nil {
            panic(err)
    }
    fmt.Println(string(res))
}

通過(guò)將Time嵌入MyTime,MyTime也可以獲得Time類型的方法集。更具體的可以看我之前寫(xiě)的另一篇文章:golang拾遺:嵌入類型

如果我實(shí)在需要派生出一種新的類型呢,通常在我們寫(xiě)一個(gè)通用模塊的時(shí)候需要隱藏實(shí)現(xiàn)的細(xì)節(jié),所以想要對(duì)原始類型進(jìn)行一定的包裝,這時(shí)該怎么辦呢?

實(shí)際上我們可以讓MyTime重新實(shí)現(xiàn)json.Marshaler:

type MyTime time.Time
 
func (m MyTime) MarshalJSON() ([]byte, error) {
    // 我圖方便就直接復(fù)用Time的了
    return time.Time(m).MarshalJSON()
}
 
func main() {
    myTime := MyTime(time.Now()) // 假設(shè)獲得的時(shí)間是 2022年7月20日20:30:00,時(shí)區(qū)UTC+8
    res, err := myTime.MarsharlJSON()
    if err != nil {
            panic(err)
    }
    fmt.Println(string(res))
}

這么做看上去違反了DRY原則,其實(shí)未必,這里只是示例寫(xiě)的爛而已,真實(shí)場(chǎng)景下往往對(duì)派生出來(lái)的自定義類型進(jìn)行一些定制,因此序列化函數(shù)里會(huì)有額外的一些操作,這樣就和DRY不沖突了。

不管哪一種方案,都可以解決問(wèn)題,根據(jù)自己的實(shí)際需求做選擇即可。

到此,關(guān)于“Golang怎么自定義類型和方法集”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI