1. 程式人生 > >淺談Go型別轉換之間的那些事

淺談Go型別轉換之間的那些事

試著答一答這些問題

s[i]和(for _,v range)的v的區別是什麼
var s string = "AB"
fmt.Println(reflect.TypeOf(s[0]))
for _, v := range s {
   fmt.Println(reflect.TypeOf(v))
}
a.(),和 a(b) 的區別是什麼?
var v interface{} = 1
var s uint8 = 1

temp1 := int(s)
temp2 := v.(int)

fmt.Println(temp1,temp2)

Go的型別系統瞭解

Go的型別

Go語言是一門靜態編譯型語言,是一門強型別語言,Go語言中型別分為兩種:命名型別(已定義型別)和未命名型別(組合型別),我舉例說一下

  1. 命名型別(已定義型別)
uint8(byte) uint16 uint32 uint64 int int8 int16 int32(rune) int64 bool string
float32 float64 complex64 complex128

上面舉例型別歸為三大類:,數值型別,字串型別, 布林值型別,我們使用type定義的任何型別也被稱為命名型別,如下

//也是命名型別
type MyBool bool 
  1. 未命名型別 (組合型別)
slice map chan function interface struct pointer

上面舉例的型別有容器型別,函式型別,指標型別,結構體型別,通道型別,介面型別

自定義型別和底層型別

Go允許通過type關鍵字定義一個型別
Go的每一個型別都一個底層型別,型別的底層型別有如下規律

  1. 每一個命名型別的底層型別都是自己
  2. 每一個組合型別的底層型別都是自己
  3. 在一個型別的宣告中,新宣告的型別和原型別的底層型別是共享的

如下程式碼,請問這段程式碼能夠編譯成功嗎?為什麼?首先這段程式碼是編譯失敗的,i的型別是MyInt,j的型別是int,雖說她們的底層型別都是int,但不能相互賦值,也就說明命名型別間是不能相互賦值的,即便是低限制往高限制賦值,比如 int32 賦給 int64也是編譯失敗的

type MyInt int
func CustomType() {
   var i MyInt = 2
   var j int = 1
   j = i
   i = j
   fmt.Println(i == j)
}

下面這段程式碼會列印這兩個變數的基本型別和底層型別,

//輸出MyInt int
fmt.Println(reflect.TypeOf(i), reflect.TypeOf(j))
//輸出int int
fmt.Println(reflect.TypeOf(i).Kind(), reflect.TypeOf(j).Kind())

我們再來看一個Demo,下面這段程式碼編譯會報錯嗎,如果把int32改成int64呢?答案是編譯報錯,改成int64也會編譯報錯,只有j和int32同時改成i和int64,才會編譯成功。因為這時m和n的底層型別是完全相同的。

type MyM struct {
   i int64
}
type MyN struct {
   j int32
}
func TestStruct() {
   n := MyN{j: 10}
   var m MyM
   m = MyM(n)
  fmt.Println(n,m)
}
如何追蹤朔源一個型別的的底層型別

如下程式碼,說說這些型別的底層型別是什麼?

type MyInt int
type I MyInt
type Ints []int
type MyInts []MyInt
type M map[string]string
type CustomM M

MyInt的底層型別是int
I的底層型別時int
Ints的底層型別是[]int
MyInts的底層型別是slice
M的底層類是map
CustomM的底層類是map

規律就是直到找到的一個內建型別(Go內建的型別)或者未命名型別(組合型別)結束,這個型別就是當前型別的底層型別

怎麼通過程式碼獲取一個型別的底層型別呢?通過下面程式碼獲取

reflect.TypeOf(variable).Kind()
類型別名

什麼是類型別名呢?Go中有兩個類型別名 byte,對應的真實型別是uint8,rune,對應的真實型別是int32,我們可以原始碼中這兩個的定義如下

// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is
// used, by convention, to distinguish byte values from 8-bit unsigned
// integer values.
type byte = uint8

// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
type rune = int32

從這個就能就能解決最開始的第一個問題,s[index]取得是字串轉換成位元組後的某一個位元組,而range指的是迴圈字串s的每一個字元(range會隱式的unicode解碼), 但字元區分字母和漢字,一個字母佔用一個位元組,一個漢字可不是了,看如下程式碼,你可以獲取byte和rune的底層型別

var r rune = 'c'
var b byte = 1
fmt.Println(reflect.TypeOf(r).Kind()) //int32
fmt.Println(reflect.TypeOf(b).Kind()) //uint8

如何定義一個類型別名呢?其實很簡單,知道怎麼定義一個型別,那麼定義一個類型別名就很簡單了,參考上面的byte和rune,如下我們為int64定義一個別名(從Go1.9開始支援),類型別名是可以被宣告在函式體內的

//相比定義一個型別多了一個=號
type alaisInt64 = int64
型別轉換和斷言

型別轉換是用來在型別不同但相互相容的型別之間的相互轉換的方式,如果不相容,則無法相互轉換,編譯會報錯,通常寫法是 a(b),把b轉換成a

型別斷言是在介面之間進行,本質也是型別轉換,寫法是a.(b),含義是把a轉換成b

如下程式碼,做一些錯誤的和正確的示範


//這個轉換時型別不同,也不相容,所以編譯報錯
s := "ab"
i := int(s)

//這個轉換型別不同,但相容,所以OK
var j int8 = 1
m := int(j)

//這個轉換是失敗的,系統會檢測到型別不匹配,直接panic
var k interface{} = "s"
l := k.(int)
//但我們可以通過一個引數來判斷,只有f為true時,才會轉換成功
l,f := k.(int)
//這個轉換是成功的
p,f := k.(string)

型別轉換的實踐,勤加練習才能理解

數字型別之間轉換

從低位轉高位沒有什麼問題,從高位轉低位時(會丟失精度),int64轉int8,這個轉換的過程如下:
128的二進位制:.........00000000_10000000
因為是從int64轉int8,所以擷取128的後八位 :10000000
此時最高位是1,表示這是一個負數,此時結果是就是:-128

//這個轉換沒有任何問題,都OK
var i int8 = 123
var j int16 = int16(i)
//這個轉換會丟失精度,從高位轉低位
var m int64 = 128
var n int8 = int8(m) //n的結果是-128,因為int8能表達的最大值是127,最小值是-128,
字串,位元組,數字,字元互相轉換
var s1,s2 string = "AbcD","1234"
//轉位元組
bs1 := []byte(s1); bs2 := []byte(s2)

//位元組陣列轉字串
s11 := string(bs1); s22 := string(bs2)
//單個位元組轉字串
ss := string(bs1[0])
fmt.Println(s11, s22, ss)

//s2轉數字 ,err 表示是否能轉換成功,比如s1就會轉換失敗
i, err := strconv.Atoi(s2)
//數字轉字串
s := strconv.Itoa(i)

//字串轉字元陣列
runes := []rune(s1)

//字元陣列轉字串
ss1 := string(runes)
//單個字元轉字串
ss2 := strconv.QuoteRune(runes[0])

//字元轉位元組
bss := make([]byte, 0)
bss = strconv.AppendQuoteRune(bss, runes[0])
fmt.Println(err, s, ss1, ss2, runes[0], bss, string(bss))
//除開rune和byte底層的型別的區別,在使用上,
//rune能處理一切的字元,而byte僅僅侷限在ascii

//整形轉位元組
x := int32(68)
bytesBuffer := bytes.NewBuffer([]byte{})
binary.Write(bytesBuffer, binary.BigEndian, x)
//位元組轉整形
var y int32
binary.Read(bytesBuffer, binary.BigEndian, &y)
介面到具體型別的轉換
//由介面型別轉換為具體的型別
var i interface{} = 1
t, f := i.(int)
if f { //轉換成功
   fmt.Println(t)
} else {//轉換失敗
   fmt.Println(reflect.TypeOf(i).Kind(), reflect.TypeOf(i))
}

歡迎關注公眾號,閱讀更多精彩文章

相關推薦

Go型別轉換之間那些

試著答一答這些問題 s[i]和(for _,v range)的v的區別是什麼 var s string = "AB" fmt.Println(reflect.TypeOf(s[0])) for _, v := range s { fmt.Println(reflect.TypeOf(v)) } a.()

關於react router 許可權那些

背景 近期做了一個 spa的單獨專案中,有個需求就是希望根據登入人來看下,這個人是不是有許可權進入當前頁面。雖然服務端做了進行介面的許可權,但是每一個路由載入的時候,都要去請求這個介面太浪費了。 需要考慮的 登入授權,使用者沒有登入只能訪問登入頁面,如果處於登入狀態則跳轉到當前使用者的預設首頁

矩陣快速冪的那些

矩陣的快速冪是用來高效地計算矩陣的高次方的。將樸素的o(n)的時間複雜度,降到log(n)。 這裡先對原理(主要運用了矩陣乘法的結合律)做下簡單形象的介紹: 一般一個矩陣的n次方,我們會通過連乘n-1次來得到它的n次冪。 但做下簡單的改進就能減少連乘的次數

JavaScript -- 資料型別及其相互轉換

ES 中有 7 種資料型別,分別是 String , Number , Boolean , Undefined , Null ,Object 以及 Symbol (在ES6中引入的概念),其中前面 5 種又被稱為基本資料型別,Object 被稱為引入資料型別,而 Symbol

運維自動化的那些事兒

運維自動化 it監控 服務流程管理 it運維 前言運維管理兜兜轉轉十幾余載,大家的運維管理再也不是小米加×××、人工費力拉線扛服務器的傳統時代,如你所知,這些年大家張口閉口談的都是運維自動化如何如何。一千個讀者就有一千個哈姆雷特,一千個運維就有一千種運維自動化想法或構建思路,小生不才,今日鬥膽

go之介面、執行緒、通道,純屬個人看法

淺談go介面、通道、執行緒 golang 接 口 Go 是靜態型別的。每一個變數有一個靜態的型別,也就是說,有一個已知型別並且在編譯時就確定下來了 type MyInt int var i int var j MyInt 那麼 i 的型別為 int 而 j 的型別為 MyInt。即使

變數型別之外的變數命名

在程式設計實踐中定義變數時,我們所能控制的無非兩點:變數型別與變數名。某種程度上,這兩者分別考驗的其實是開發者的數學水平與語文水平。在今天,即便已經有了非常高大上的型別系統,「名不副實」的變數名仍然經常能對開發者造成困擾。那麼,我們有什麼理論能用來指導變數命名呢? 在電腦科學的萌芽時代,變數名和變數型別之間

序列訊號 轉換成 並行訊號 原理

注 :  文中講述的原理是推理和探討 , 和現實中的實現不一定完全相同 。   開始之前, 可以先參考看看我之前寫的兩篇文章 : 《設計一個 硬體 實現的 Dictionary(字典)》  https://www.cnblogs.com/KSongKing/p/1

C++數值與字串相互轉換那些(一)字串轉數值(轉載請註明)

以前一門心思搞演算法,這個東西覺得自己寫個函式就能實現的事,但是到了公司後才發現同事寫的程式碼裡面,呼叫各種庫函式、window API、流來實現。什麼都不懂的我表示鴨梨很大,今天翻了翻資料瞭解了下各種方法的使用方法、區別以及適用範圍,寫成了這篇又長又臭又沒條理的東西。 注

型別和引用型別在堆和棧中的儲存一

首先,讓我們來簡單瞭解一下什麼是“棧”(stack),什麼是“堆”(heap)。“棧”其實就是一種後入先出(LIFO)的資料結構。在我們.NET Framework裡面,由CLR負責管理,我們程式設計師不用去擔心垃圾回收的問題;每一個執行緒都有自己的專屬的“棧”。“堆”的存

SQLServer行列轉換PIVOT函式的使用

以學生表舉個例子,展現學生的各門學科和成績,我們先新建一張表(表中插入測試值的時候用到了rand取隨機數,沒用過的可以瞭解下-->點選開啟): Create Table Students(Name varchar(10), Subject Nvarchar(10),S

關於型別轉換這件

有同事和我討論了一個問題:怎樣把Date轉換成正確的格式給前端?當然可以手動轉換,但是他更期望通過框架配置的方式解決。關於這個問題有兩種思路:1、在orm框架中轉換;2、在json框架中轉換。在這裡我想分享一下自己的理解和經驗。 誰可以做? 型別轉換不是什麼神奇的事,看原始碼就會發現,它只是一堆的if

網路地址轉換(NAT)技術與內網、外網

前言  我們現在常使用的IP地址是IPv4地址,由四組0-255的十進位制數字組成,中間以小數點分隔。Internet上的每一臺主機或者路由器都至少有一個IP地址。IP地址(IPv4地址,下文IP地址預設指IPv4)的長度是32位,總數為2的32次方,大約43億個。  

策略型別遊戲

作者:區宗思 前言: 由於作者本人三年參與的兩款專案中都是策略型別遊戲,鑑於對自己總結和反思,遂決定寫下本文,希望從中能看到些許沉澱繼而提高自身設計水平 正文 什麼是策略遊戲 首先我們談談策略遊戲的概念,很多人將策略遊戲的稱為SLG遊戲,但事實上策略遊戲是SLG

Android學習筆記之@id與@+id之間的區別

<Button android:id="@+id/button3" //定義了一個Button取名為button3 android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_cen

20190608_go&java差異(三)

20190608_淺談go&java差異(三) 轉載請註明出處https://www.cnblogs.com/funnyzpc/p/10990703.html 第三節內容概覽 多執行緒通訊(執行緒安全型別 chan) struct(結構體) 與 物件實體類 異常(panic 與 throw) 陣列切

Go語言的Goroutine和協程

0x00.前言 前面寫了一篇初識Go語言和大家一起學習了Go語言的巨大潛力、語言簡史、殺手鐗特性等,感興趣的讀者可以回顧一下。 今天來學習Go語言的Goroutine機制,這也可能是Go語言最為吸引人的特性了,理解它對於掌握Go語言大有裨益,話不多說開始吧! 通過本文你將瞭解到以下內容: 什麼是協程以及橫向

C#語言中的各種數據類型,與數據類型之間轉換

優化配置 line com 歸類 浮點 初學者 結構 ali 順序 什麽是數據類型? 數據類型,百度百科是這樣解釋的:數據類型在數據結構中的定義是一個值的集合以及定義在這個值集上的一組操作。這樣的解釋對於一個初學者來說未必太過於深奧。 簡單點說,數據類型就是不同長度的數據的

Virtual Dom 的那些

https 操作 封裝 In true 解決 port false his 背景 我們都知道頻繁的dom給我們帶來的代價是昂貴的,例如我們有時候需要去更新Table 的部分數據,必須去重新重繪表格,這代價實在是太大了,相比於頻繁的手動去操作dom而帶來性能問題,vdom很

C++類(4)--隱式類型別轉換

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!