Golang基本類型整理

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。


總是用的用的就模糊了,不知道基本的類型有哪些,看來要反反覆複弄幾次。

Golang基本類型整理

基本類型以及定義變數需要注意的

對於基本類型的介紹,感覺這個部落格講的比較透徹,基本上都是從源碼的角度來入手分析的,自己並沒有理解到這麼深入的程度,這裡跟著文章過一下,挑一些主要的部分記錄一下。
在go語言中,資料類型分為靜態類型底層類型,感覺底層類型是golang實現的時候所使用的c語言的類型,而靜態類型僅僅是針對於go語言本身而言所定義好的類型。具體的資訊可以查看$GOROOT/src/runtime/runtime.h 可以看到golang中的byte類型,通過typedef uint8 byte來定義,這樣在golang中直接使用反射

package mainimport ("fmt""reflect")func main() {var b byte = 'D'fmt.Println(reflect.TypeOf(b))}//output: uint8

這裡通過反射得到的是uint8,8個bit表示的不帶正負號的整數類型。

string

可以看到string類型的結構體就是



struct String
{
byte* str;
intgo len;
}


字元數組儲存實際的資料,len儲存長度。通過結構體就可以看到,定義好string變數之後是不能動態增加的,因為len已經寫入進去了。

strconv golang中string與基礎資料型別 (Elementary Data Type)之間的轉化
fmt.sprintf 函數也可以將interface類型轉化為string類型
http://blog.csdn.net/siddontang/article/details/23541587
string之間的類型的比較 strings.Equal??

slice

slice為了實現動態增長,又多添加了一個元素cap,這個元素表示已經分配的元素,就是相當於總的空間,len表示對於使用者使用而言這個slice的實際長度。這個和java中的Arraylist感覺有點類似,就是先分配一個空間,之後看空間不夠的話,再動態擴充,但是擴充的過程對使用者來說是不可見的:



struct Slice
{
byte* array;
uintgo len;
uintgo cap;
}

注意slice的第一個參數是一個byte指標,實際上指向的就是底層數組的對應的位置。
這裡補充一點,關於初始化的問題,make函數可以用於對slice、map 以及 channel對象進行初始化操作,這三個對象的背後使用了必須要初始化的結構,比如對於slice , 要是不初始化的話,各個值都是nil 顯然是沒沒法使用的。關於make與new的區別可以參考這個部落格

下面是一個slice和string轉化的例子,可以用來更好地瞭解slice與string以及底層數組的關係,不同的產生slice的方式,內部的操作是不同的。

package mainimport ("fmt""unsafe")func main() {//example 1var slice []int32 = make([]int32, 5, 10)//p是指向slice類型的一個指標//這裡是類比一個slice類型 然後轉化過來p := (*struct {array uintptrlen   intcap   int})(unsafe.Pointer(&slice))//para v will print the go type//If the value is a struct,//the %+v variant will include the struct’s field namesfmt.Printf("output:%+v\n", p)//example 2//聲明數組的時候 沒有顯式指定數組的長度 而是通過特殊的標記 ...//告訴編譯器 讓編譯器去自動地計算var array = [...]int32{1, 2, 3, 4, 5}//數組進行截取 轉化為slice 此時自動給len 和 cap 賦了值var sliceb = array[2:4]fmt.Printf("before modify: array=%+v, slice = %+v\n", array, sliceb)//此時 slice 中用的底層數組和 array 是同一個//此時 slice[0]的位置 指向的實際是底層數組中 元素3所在的位置sliceb[0] = 678fmt.Printf("after modify: array=%+v, slice = %+v\n", array, sliceb)//example 3//注意 如果使用的是append方式產生新的slice//就不會有類似的效果 因為採用append方式會分配新的底層數組array = [...]int32{1, 2, 3, 4, 5}var slicec = array[1:3]slicec = append(slicec, 6, 7, 8)//可以對比這次結果 發現 兩次array的輸出是一樣的 並沒有因為 slice的修改而對array造成影響fmt.Printf("before modify: array=%+v, slice = %+v\n", array, sliceb)slicec[0] = 123fmt.Printf("after modify: array=%+v, slice = %+v\n", array, sliceb)//注意 從slice 產生slice 的時候原理也是類似的 直接用slicea=sliceb[para1:para2]的文法//使用的是同一個底層的數組 要是通過append方式 則會重新分配底層數組}/* 輸出:output:&{array:8725963136 len:5 cap:10}before modify: array=[1 2 3 4 5], slice = [3 4]after modify: array=[1 2 678 4 5], slice = [678 4]before modify: array=[1 2 3 4 5], slice = [3 4]after modify: array=[1 2 3 4 5], slice = [3 4]*/

map

map類型、slice類型、以及通道類型,都是參考型別,參考型別就是說這個結構總會持有一個指標,保持對底層某個結構的引用。作為對比,數群組類型就是實值型別。對於參考型別,在使用的時候,具體聲明了之後,還要進行初始化,只有這樣才能建立起和底層資料結構之間的聯絡。使用字典的時候,要不就直接聲明+初始化,要不就聲明之後使用make初始化。

//直接聲明加賦值mb := map[int]string{1: "hello", 2: "go"}//直接聲明並初始化m := make(map[string]int)

之後就可以直接使用了。

array

array定義的時候,通過[n]類型進行定義,可以在定義的時候就把值賦進去,比如[3]strng{"a","b","c"} 還可以在[]中使用...標記,這樣編譯器會自動識別數組的長度,並且進行初始化。

interface以及類型轉化

介面類型的實現比較複雜,整理出幾條關於介面的要點,實際中,關於斷言運算式的地方總是用的不太好。這個文章關於斷言說的比較好。這個文章介紹也比較好。

go 語言是編譯型的,有一次寫了這樣的代碼 :

containerpid = fmt.Sprintf("%d", containerpid)

之後是對containerpid進行一些字串的操作,比如字串的拼接,在編譯的時候就會報如下的錯誤:

invalid operation: "teststring" + containerpid (mismatched types string and interface {})

主要原因是因為go並不是那種動態執行的語言,因為是要先編譯好再執行,在編譯器看來,這個contaierid還是沒有轉化成strig類型(??) 除非不使用同名的變數,另外新起一個變數名字,這樣編譯時間才能通過。

(1)普通類型向介面類型的轉化是隱式的,介面類型向普通類型轉換需要類型斷言。由於interface{}包含了0個方法,所以任何類型都實現了interface{}介面,這就是為什麼可以將任意類型值賦值給interface{}類型的變數,包括nil,下面是隱式轉化的例子:

package mainimport ("fmt""reflect")func main() {var val interface{} = "hello"fmt.Println(reflect.TypeOf(val)) //output: stringfmt.Println(val)val = []byte{'a', 'b', 'c'}fmt.Println(reflect.TypeOf(val)) //output: stringfmt.Println(val)}

(2)在顯式的類型轉化中,一種方式是使用comma-ok的斷言文法,由於golang中不像java哪有那樣採用泛型聲明的方式,因此好多時候不能確定這個類型到底實現了哪些介面,於是可以採用斷言運算式,注意 x.(T) 這樣的斷言類型轉化,x 必須是一個介面類型的變數,T 是一個對應的實際的類型,比如下面這個程式,產生一個interface{}類型的數組,之後通過隱式轉化,可以往裡面放入任何類型的變數,之後還可以再使用用斷言運算式檢測對應的類型。

package mainimport ("fmt")type Test []interface{}func main() {test := make(Test, 5)test[0] = "a"test[1] = 2test[2] = truetest[3] = []byte("test")for index, element := range test {if value, ok := element.(string); ok {fmt.Printf("test[%d] is type of string the value is %v\n", index, value)} else if value, ok := element.([]byte); ok {fmt.Printf("test[%d] is type of string the []byte is %v\n", index, value)}}}

(3)在使用執行個體實現interface的時候,還要注意值方法和指標方法的區別。interface這裡的使用,多多少少可以看出一些動態語言的設計風格,就是所謂的"鴨子類型",即不是通過顯式的指明繼承自某個介面或者類,而是由當前類型實現方法的屬性集合來決定。理解上就是,感覺比靜態類型逼格高一點,總之我的interface就寫在那裡,你要是想繼承我,使用我的方法,就把這些方法實現了就行,並不要求你顯示的聲明,繼承自我,怎樣怎樣。有些動態類型的語言在運行期才能進行類型的文法檢測,go語言在編譯期間就可以檢測。比如下面這個例子:

package mainimport ("fmt""reflect""sort")type Sortable interface {Len() intLess(i, j int) boolSwap(i, j int)}type SortStringA [3]stringtype SortStringB [3]stringfunc (self SortStringA) Len() int {return len(self)}func (self SortStringA) Less(i, j int) bool {return self[i] < self[j]}func (self SortStringA) Swap(i, j int) {self[i], self[j] = self[j], self[i]}func (self SortStringA) Sort() {sort.Sort(self)}func (self *SortStringB) Len() int {return len(self)}func (self *SortStringB) Less(i, j int) bool {return self[i] < self[j]}func (self *SortStringB) Swap(i, j int) {self[i], self[j] = self[j], self[i]}func (self *SortStringB) Sort() {//調用sort包中的 Sort方法 傳入的參數只要是一個實現了 sort.interface的類型的執行個體即可sort.Sort(self)}func main() {sa := SortStringA{"2", "3", "1"}sb1 := &SortStringB{"2", "3", "1"}sb2 := SortStringB{"2", "3", "1"}fmt.Println(reflect.TypeOf(sa))sorta, ok := interface{}(sa).(Sortable)fmt.Println(reflect.TypeOf(sorta))fmt.Println(ok) //output:truesa.Sort()fmt.Printf("SortStringA after sort %v:\n", sa)sort.Sort(sa)fmt.Printf("SortStringA after sort %v:\n", sa)//在golang 的源碼包中fmt.Println(reflect.TypeOf(sb1))sort.Sort(sb1)sorted := sort.IsSorted(sb1)fmt.Printf("sb1 (type:SortStringB) after sort %v :, is sorted : %v \n", *sb1, sorted)sb2.Sort()fmt.Printf("sb2 (type:SortStringB) after sort : %v\n", sb2)}/*output:main.SortStringAmain.SortStringAtrueSortStringA after sort [2 3 1]:SortStringA after sort [2 3 1]:*main.SortStringBsb1 (type:SortStringB) after sort [1 2 3] :, is sorted : true sb2 (type:SortStringB) after sort : [1 2 3]

感覺這一段代碼還是能夠說明一些問題的:

  • 首先定義了一個sortstable的 介面 這個介面中有三個方法,這三個方法同sort包中的基本類型sort.interface中定義的三個方法是一樣的,之後分別用值方法與指標方法對這三個方法進行了實現。

  • 可以看到,採用值方法時,因為方法調用的時候,採用的是指傳遞,原來的變數中sa的資料並沒有排序。同時還測試了一下斷言運算式,通過comma-ok的運算式,sa類型可以轉化為sortable類型,這裡體現了其動態性,但是採用反射輸出的時候,還顯示的是其本身聲明時候的類型main.SortStringA,這裡又體現了其強靜態類型(類型一但確定就不再改變),兩者結合起來使Go語言在保持強靜態類型的安全和高效的同時,也能靈活安全地在不同相容類型之間轉換。(這個文章講一些interface原理性的內容,比較好)注意裡面的典型的interface+switch+斷言的使用方式。

  • 由於sortable中的三個方法和sort.interface中定義的三個方法一樣,因此sa sb相當雩都實現了sort.interface方法,可以直接調用sort包中的函數。但是要注意,具體實現的時候,前一個是SortStringA本身實現了三個方法,後一個是SortStringB指標實現了三個方法,因此 SortStringB指標類型實現了 sort.Interface 調用的時候傳遞過去的要是指標才行,否則編譯報錯。比如直接sort.Sort(sb2)會報錯

SortStringB does not implement sort.Interface (Len method has pointer receiver)

參考資源

http://www.infoq.com/cn/articles/go-interface-talk

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.