golang 型別斷言 VS 型別轉換(七)

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

依然是介面型別,但是我們可以透過型別斷言獲取字串型別:

golang 型別斷言 VS 型別轉換(七)

注意

:這裡我們要考慮一個問題:在使用

型別斷言

時是否一定要提前知道

介面變數

原始型別

呢?如果我們不知道

原始型別

,或者介面變數不具備我們想目標型別會有什麼結果呢?

我們來關注一下型別斷言的

另外一種用法

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

golang 型別斷言 VS 型別轉換(七)

什麼時候我們可以使用型別轉換?

只有當兩個型別的

底層資料結構

相同的時候,才可以使用

型別轉換

,在做

型別轉換

時不會檢查型別的方法。讓我們來看一個結構體的例子:

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}

它們就可以相互進行型別轉換

golang 型別斷言 VS 型別轉換(七)

如果像上面這種宣告多個具有相同的

底層資料結構

的結構體型別,可以有一個簡潔的寫法:

type person struct { name string age int}type child person

為什麼叫做型別轉換

正如上面提到的不同的型別,在其上的限制和方法是不同的,即使它們有著相同的底層資料結構。在我們使用 golang 的型別轉換時我們改變的是這個

變數

的行為(因為不同的型別有不同的函式),而不是僅僅暴露這個變數的

原始型別

(後者是型別斷言做的事)。另外,如果兩個型別不能相互轉換,golang 會在編譯時報錯,不像型別斷言時在執行時報錯。

總結

型別轉換

型別斷言

不僅僅是語法上的不同,透過這兩個概念進一步強調了 golang 中

介面型別

非介面型別(具體型別)不同。介面型別沒有底層資料結構,它僅僅是暴露了預先定義在

具體型別中的方法。

型別斷言

是獲取隱藏在

介面型別

變數之下的具體型別,

型別轉換

是改變我們操作變數

底層資料

方法