1. 程式人生 > >[Golang學習筆記] 06 程序實體3 類型斷言和類型轉換

[Golang學習筆記] 06 程序實體3 類型斷言和類型轉換

問題 怎麽 values ima spa 極客 內部 png nta

類型斷言:

語法:
<目標類型的值>,<布爾參數> := <表達式>.( 目標類型 ) // 安全類型斷言
<目標類型的值> := <表達式>.( 目標類型 )  //非安全類型斷言

x.(T),這裏x表示一個接口的類型,T表示一個類型(也可為接口類型)。
一個類型斷言檢查一個接口對象x的動態類型是否和斷言的類型T匹配。

類型斷言分兩種情況:
第一種,如果斷言的類型T是一個具體類型,類型斷言x.(T)就檢查x的動態類型是否和T的類型相同。

如果這個檢查成功了,類型斷言的結果是一個類型為T的對象,該對象的值為接口變量x的動態值。換句話說,具體類型的類型斷言從它的操作對象中獲得具體的值。
如果檢查失敗,接下來這個操作會拋出panic,除非用兩個變量來接收檢查結果,如:f, ok := w.(*os.File)
第二種,如果斷言的類型T是一個接口類型,類型斷言x.(T)檢查x的動態類型是否滿足T接口。

如果這個檢查成功,則檢查結果的接口值的動態類型和動態值不變,但是該接口值的類型被轉換為接口類型T。換句話說,對一個接口類型的類型斷言改變了類型的表述方式,改變了可以獲取的方法集合(通常更大),但是它保護了接口值內部的動態類型和值的部分。
如果檢查失敗,接下來這個操作會拋出panic,除非用兩個變量來接收檢查結果,如:f, ok := w.(io.ReadWriter)
註意:

如果斷言的操作對象x是一個nil接口值,那麽不論被斷言的類型T是什麽這個類型斷言都會失敗。
我們幾乎不需要對一個更少限制性的接口類型(更少的方法集合)做斷言,因為它表現的就像賦值操作一樣,除了對於nil接口值的情況。

面試題:怎麽樣判斷一個變量的類型?

package main

import "fmt"

var container = []string{"zero", "one", "two"}

func main() {
    container := map[int]string{0: "zero", 1: "one", 2: "two"}
    fmt.Printf("The element is %q.\n", container[1])
}

回答:

使用“類型斷言”表達式。
value, ok := interface{}(container).([]string)
在賦值符號的右邊,是一個類型斷言表達式。
把container變量的值轉換為空接口值的interface{}(container)。
以及一個用於判斷前者類型是否為切片類型[]string的 .([]string)。

解析:
類型斷言表達式的語法形式是x.(T)。其中x代表要被判定類型的值。這個值的類型必須是接口類型的,不過無關哪個接口類型。
所以如果container不是任何的接口類型時,需要先轉換成某個接口類型。
如果是,這個斷言表達是可以寫成container.([]string)。

技術分享圖片

最右邊的圓括號中 []string 是一個類型字面量。類型字面量,就是用來表示數據類型本身的若幹個字符。
比如,string是表示字符類型的字面量,uint8是表示8位無符號整數類型的字面量。

一對不包裹任何東西的花括號,既可以代表空代碼塊,也可以表示不包含任何內容的數據結構(數據類型)。

struct{}表示不包含任何字段和方法的空結構體類型。
[]string{}表示空切片值(數據類型)。
map[int]string{}表示空字典值,類型字面量是表示數據類型本身的若幹個字符,如string是字符串類型的字面量,uint8表示8位無符號整數類型的字面量。
其中[]string表示元素類型為string的切片類型;
map[int]string表示鍵類型為int,值類型為string的字典類型。


類型轉換:

語法:<結果類型> := <目標類型> ( <表達式> )
T(x)
T表示類型,x可以是變量,也可以是一個代表值的字面量(比如1.23和struct{})

var a int = 9
var b := float(a)

類型轉換是用來在不同但相互兼容的類型之間的相互轉換的方式,所以,當類型不兼容的時候,是無法轉換的。

int<--->string

//string到int
value_int,err:=strconv.Atoi(string)
//int到string
str:=strconv.Itoa(value_int)

int64<--->string

//string到int64
value_int64, err := strconv.ParseInt(string, 10, 64)
//int64到string,需註意下面轉換規定
//FormatInt returns the string representation of i in the given base, for 2 <= base <= 36.
//The result uses the lower-case letters ‘a‘ to ‘z‘ for digit values >= 10
str:=strconv.FormatInt(value_int64, 10) //FormatInt第二個參數表示進制,10表示十進制

float<--->string

//float轉string
v := 3.1415926535
s1 := strconv.FormatFloat(v, E, -1, 32)//float32 
s2 := strconv.FormatFloat(v, E, -1, 64)//float64
//第二個參數可選‘f‘/‘e‘/‘E‘等,含義如下:
// ‘b‘ (-ddddp±ddd,二進制指數)
// ‘e‘ (-d.dddde±dd,十進制指數)
// ‘E‘ (-d.ddddE±dd,十進制指數)
// ‘f‘ (-ddd.dddd,沒有指數)
// ‘g‘ (‘e‘:大指數,‘f‘:其它情況)
// ‘G‘ (‘E‘:大指數,‘f‘:其它情況)
 
//string轉float
s := "3.1415926535"
v1, err := strconv.ParseFloat(v, 32)
v2, err := strconv.ParseFloat(v, 64)

float<--->int

var a int64
a = 1
var b float64
b = 2.000
 
//a -- float64
c := float64(a)
 
//b -- int64
d := int64(b)

思考題:1. 你認為類型轉換規則中有哪些值得註意的地方?

1、 對於整數類型值、整數常量之間的類型轉換,原則上只要源值在目標類型的可表示範圍內就是合法的。
源整數類型的可表示範圍較大,而目標類型的可表示範圍較小的情況,比如把int16轉換為int8,如:

var srcInt = int16(-255)
dstInt := int8(srcInt)

其中變量srcInt的值是int16類型的-255轉化來的,而變量dstInt的值是srcInt轉換來的,類型是int8。

此例子中,int16類型比int8的範圍要大,運行結果為:1.
整數在Go中是以補碼的形式存儲的,補碼其實就是原碼個位求反再加1.
int16類型的值-255的補碼是1111111100000001,如果轉換為int8類型,那麽Go就會把在較高位置(最左位置)上的8位二進制直接截掉,從而得到00000001.
又由於最左邊一位是0,表示它是個正整數,而正整數的補碼就等於其原碼,所以這裏輸出1.

2、雖然直接把一個整數值轉為一個string類型的值是可行的,但是被轉換的整數值必須要能代表一個有效的Unicode代碼點。

3、string類型與各種切片類型之間的互轉。
一個值從string類型向[]byte類型轉換時,代表著以UTF-8編碼的字符串會被拆分成獨立的字節,兩個字節代表一個漢字。
一個值從string類型向[]rune類型轉換時,代表著字符串會被拆分成一個個的Unicode字符,一個字符代表一個漢字。

2. 什麽是別名類型?什麽是潛在類型?

別名類型是type聲明自定義類型的一種,如:

type MyString = string

表示MyString是string類型的別名類型,與其源類型是完全相同的。
別名類型主要是為了代碼重構而存在的。
byte是uint8的別名類型,rune是int32的別名類型。

潛在類型是某個類型在本質上是哪個類型或者是哪個類型的集合,如:

type MyString2 string

潛在類型的定義之間沒有“=”號
潛在類型的值之間是可以進行類型轉換的,如本例中MyString2類型的值可以跟string類型的值互相轉換,但是集合類的類型[]MyString2與[]string就不行,

因為他們的潛在類型就不同了,分別是MyString2和string。

即使兩個類型的潛在類型相同,他們的值之間也不能進行判斷或比較,他們的變量之間也不能互相賦值。

技術分享圖片

3. 別名類型在代碼重構過程中可以起到哪些作用嗎?

類型別名是 Go 1.9 版本添加的新功能。主要用於代碼升級、遷移中類型的兼容性問題。
下一篇分析類型別名和類型定義。

總結
類型斷言表達式可用來判斷變量是哪個類型,把結果賦給兩個變量,要保證被判斷的變量是接口類型的,這可能會用到類型轉換表達式。
要搞清楚別名類型聲明與類型再定義之間的區別。

本學習筆記僅為了總結自己學到的Go語言核心知識,方便以後回憶,文中部分內容摘錄自極客時間的《Go語言核心36講》專欄,如有侵權,請聯系我刪除。

[Golang學習筆記] 06 程序實體3 類型斷言和類型轉換