golang 中
型別斷言
和
型別轉換
兩個概念很容易困惑,它們看上去提供了相同的功能(把變數從一個型別轉到型別)。但是 golang 為什麼會有兩個功能相似的概念呢?那麼在本文中,我們將瞭解
型別斷言
和
型別轉換
本質的區別,並深入瞭解在 go 中使用它們會發生什麼?
原文:https://www。sohamkamani。com/golang/type-assertions-vs-type-conversions/
首先讓我們來看一下在 go 中如何使用它們:
型別斷言
var greeting interface{} = “hello world”greetingStr := greeting。(string)
型別轉換
greeting := []byte(“hello world”)greetingStr := string(greeting)
最明顯的不同是它們有著不同的語法:
variable。(type)
vs
type(variable)
。
型別斷言
型別斷言
顧名思義,它是用來
斷言
變數具有哪些型別的。
型別斷言
只能作用在
介面
上。在上面
型別斷言
的例子中
greeting
就是一個
介面(interface{})型別,我們給他分配了
字串(string)
型別的變數,那麼我們現在可以說greeting具有了
字串型別
,雖然
greeting
依然是介面型別,但是我們可以透過型別斷言獲取字串型別:
注意
:這裡我們要考慮一個問題:在使用
型別斷言
時是否一定要提前知道
介面變數
的
原始型別
呢?如果我們不知道
原始型別
,或者介面變數不具備我們想目標型別會有什麼結果呢?
我們來關注一下型別斷言的
另外一種用法
:
var greeting interface{} = “42”greetingStr, ok := greeting。(string)
這種用法,型別斷言有兩個返回值,第一個值
ok
時
bool
型別,當斷言成功時返回
true
,否則返回
false
。如果我們採用第一中寫法(只有一個返回值),在發生斷言錯誤時會丟擲
panic
。
注意
:上面這些表明了型別斷言是在程式
執行時
執行的。
型別斷言之 type switch
當我們不確定一個介面的底層變數型別時,這時候
type switch
就是一個很好的語法結構
var greeting interface{} = 42switch 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”)}
什麼是斷言
上面的例子中看上去我們是把變數
greeing
從
interface{}
型別轉為了
string
或者
int
,但是實際上
greeing
的型別是固定的,依然是它初始化時的型別。將
greeing
分配給介面型別時,不會更改其基礎型別。 同樣的,當我們斷言它的型別時,我們只是在使用整個原始型別的功能,而不是介面暴露的有限方法(函式)。
型別轉換
在講解型別轉換前,首先讓我們來理解一下什麼是型別?golang 中型別定義了兩個事情:
資料結構:變數在底層以什麼形式儲存
行為:變數具有哪些方法或者說是函式
golang 中變數型別可以有兩類:
基礎型別
和
混合型別
,
基礎型別
有
string
,
int
等等;
混合型別
包含:
struct
,
map
,
slice
等。
基於
基礎型別
我們還可以宣告一個新的型別:
// myInt 是一個新的變數型別,它的基礎型別是 inttype myInt int// 函式 AddOne 工作在 myInt 上,和 int 沒有任何關係func (i myInt) AddOne() myInt { return i + 1}func main() { var i myInt = 4 fmt。Println(i。AddOne())}
我們基於
基礎型別
int
聲明瞭一個新的型別
myInt
,它們
底層的資料結構
都是一樣的,但是
myInt
多了一個函式 AddOne。因為它們的底層型別是一樣的,我們可以使用型別轉換,把變數從一種型別轉為另一種型別
var i myInt = 4originalInt := int(i)
上面
i
的型別是
myint
,
originalInt
的型別是
int
。
什麼時候我們可以使用型別轉換?
只有當兩個型別的
底層資料結構
相同的時候,才可以使用
型別轉換
,在做
型別轉換
時不會檢查型別的方法。讓我們來看一個結構體的例子:
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)” would result in a compilation error fmt。Println(bob, babyBob)}
上面程式碼中
person
和
child
有著相同的
底層資料結構
:
struct { name string age int}
它們就可以相互進行型別轉換
如果像上面這種宣告多個具有相同的
底層資料結構
的結構體型別,可以有一個簡潔的寫法:
type person struct { name string age int}type child person
為什麼叫做型別轉換
正如上面提到的不同的型別,在其上的限制和方法是不同的,即使它們有著相同的底層資料結構。在我們使用 golang 的型別轉換時我們改變的是這個
變數
的行為(因為不同的型別有不同的函式),而不是僅僅暴露這個變數的
原始型別
(後者是型別斷言做的事)。另外,如果兩個型別不能相互轉換,golang 會在編譯時報錯,不像型別斷言時在執行時報錯。
總結
型別轉換
和
型別斷言
不僅僅是語法上的不同,透過這兩個概念進一步強調了 golang 中
介面型別
和
非介面型別(具體型別)不同。介面型別沒有底層資料結構,它僅僅是暴露了預先定義在
具體型別中的方法。
型別斷言
是獲取隱藏在
介面型別
變數之下的具體型別,
型別轉換
是改變我們操作變數
底層資料
的
方法
。