這是一篇譯文,原文出處:https://www.sohamkamani.com/golang/type-assertions-vs-type-conversions/。
在Go中,類型斷言和類型轉換是一個令人困惑的事情,他們似乎都在做同樣的事情。
這篇文章,我們將看到類型斷言和類型轉換實際上是如何截然不同的,並深入了解在Go中使用它們會發生什麼。
首先,讓我們看看它們長什麼樣……
下面是一個類型斷言的例子:
var greeting interface{} = "hello world"greetingStr := greeting.(string)接著看一個類型轉換的例子:
greeting := []byte("hello world")greetingStr := string(greeting)最明顯的不同點是他們具有不同的語法(variable.(type) vs type(variable) )。接下來,我們進一步去研究。
類型斷言
顧名思義,類型斷言用於斷言變量是屬於某種類型。類型斷言只能發生在interface{}類型上。
上面類型斷言的例子,greeting是一個interface{}類型,我們為其分配了一個字符串。現在,我們可以認為greeting實際上是一個string,但是對外展示的是一個interface{}。
如果我們想獲取greeting的原始類型,那麼我們可以斷言它是個string,並且此斷言操作會返回其string類型。
這意味著在做類型斷言的時候,我們應該知道任何變量的基礎類型。但是情況並非總是這樣的,這就是為什麼類型斷言操作實際上還返回了第二個可選值的原因。
var greeting interface{} = "42"greetingStr, ok := greeting.(string)第二個值是一個布爾值,如果斷言正確,返回 true ,否則返回 false。
另外,類型斷言是在程序運行時執行。類型判斷
類型判斷是一個很實用的構造。當你不確定interface{}真正類型的時候,可以使用它。
var greeting interface{} = 42
switch g := greeting.(type) { case string: fmt.Println("g is a string with length", len(g)) case int: fmt.Println("g is an integer, whose value is", g) default: fmt.Println("I don't know what g is")}為什麼需要斷言
在上面的例子中,我們似乎在將greeting從interface{}轉換成int類型或者string類型。但是greeting的類型是固定,並且和初始化期間聲明時的內容一樣。
當我們把greeting分配給interface{}類型的時候,請勿修改其原始類型。同樣,當我們斷言類型的時候,我們只是使用了原始類型功能,而不是使用interface公開的有限方法。
類型轉換首先,我們花點時間了解一下什麼是 「類型」。在 Go 每種類型都定義了兩件事:
變量的存儲方式 (存儲結構)
你可以使用變量做什麼 (可以使用的方法和函數)
這裡介紹了基本類型,包括了string和int。以及一些複合類型,比如structmaparray和slice。
你可以從基本類型或通過創建複合類型來聲明一個新類型。
// `myInt` 是一個新類型,它的基類型是 `int`type myInt int
// AddOne 方法適用於 `myInt` 類型,不適用於 `int` 類型func (i myInt) AddOne() myInt { return i + 1}
func main() { var i myInt = 4 fmt.Println(i.AddOne())}當我們聲明一個myInt類型,我們可以將變量數據基於基本的int類型,但是如果要進行變量修改,我們可以通過myInt類型變量進行操作 (通過在myInt上面聲明一個新方法)。
由於myInt 的類型基於int,意味著他們的底層基礎類型是一樣的。因此這些類型的變量可以相互轉換。
var i myInt = 4originalInt := int(i)上面i的類型是myInt,originalInt的類型是int。
什麼時候使用類型轉換?
只有當基礎數據結構類型相同,類型之間才可以相互轉換。來看一個使用struct例子。
type person struct { name string age int}
type child struct { name string age int}
type pet { name string}
func main() { bob := person{ name: "bob", age: 15, } babyBob := child(bob) // "babyBob := pet(bob)" 會導致編譯錯誤 fmt.Println(bob, babyBob)}在這裡,person 和 child 擁有相同的數據結構,即:
struct { name string age int}因此他們可以相互轉換。
type可用於聲明具有相同數據結構的多種類型。這只是意味著child和person基於相同的數據結構 (類似於之前的int和myInt)。
類型為什麼稱為轉換
就像上面說的,雖然不同類型的基礎結構可能相同,但是他們可能也具有不同的限制和方法。當我們從一種類型轉換成另一種類型時,會改變對類型的處理方式,而不是像類型斷言那樣僅公開其基礎類型,這就是他們本質的差別。(ps:這句話是我自己加的)。
如果嘗試去轉換錯誤的類型,類型轉換會提示編譯錯誤,這和類型斷言所提供的運行時通過返回值判斷錯誤,完全相反。
類型結論類型斷言和類型轉換有著比語法層面上更根本的區別。它還強調了在Go中接口類型 (interface) 和非接口類型之間的區別。
接口類型沒有任何數據結構,而是公開了已有的具體類型 (具有底層數據結構) 的一些方法。
類型斷言引出了接口的具體類型,而類型轉換改變了在具有相同數據結構的兩個具體類型之間使用變量的方式。如果文章對你有所幫助,點讚、轉發、留言都是一種支持!