兄弟連Go語言培訓課程體系設計架構包括了區塊鏈的基礎語言Go語言、區塊鏈後端技術體系、區塊鏈公鏈、區塊鏈分布式應用開發等內容講解,以及到最後的面試指導和項目實戰。課程由清華微軟Google名師團隊精心打造,曆時半年時間共同研發而出。
先介紹一下go語言的類型系統
Golang中的類型系統
類型系統是指一個語言的類型體繫結構。一個典型的類型系統通常包含如下基本內容:
q基礎類型,如byte、int、bool、float等;
q複合類型,如數組、結構體、指標等;
q可以指向任意對象的類型(Any類型);
q值語義和引用語義;
q物件導向,即所有具備物件導向特徵(比如成員方法)的類型;
q介面。
Go語言中的大多數類型都是值語義,並且都可以包含對應的操作方法。在需要的時候,你可以給任何類型(包括內建類型)“增加”新方法。而在實現某個介面時,無需從該介面繼承(事實上,Go語言根本就不支援物件導向思想中的繼承文法),只需要實現該介面要求的所有方法即可。任何類型都可以被Any類型引用。Any類型就是空介面,即interface{}。
什麼是結構體
結構體(struct)是使用者自訂的類型,它代表若干欄位的集合,可以用於描述一個實體物件,類似java中的class,是golang物件導向編程的基礎類型。
結構體的概念在軟體工程上舊的術語叫ADT(抽象資料類型:AbstractDataType)。在C++它也存在,並且名字也是struct,在物件導向的程式設計語言中,跟一個無方法的輕量級類一樣。因為Go語言中沒有類的概念,所以在Go中結構體有著更為重要的地位。
如何定義一個結構體
typeCoordinatestruct{
X,Yfloat32
}
文法:typestruct{}
上述代碼定義個一個名為Coordinate的結構體,裡麵包括了兩個float32的變數X,Y,該結構體可用於表示一個平面座標。
添加對象方法
其他的經典物件導向語言,如java,C#,定義對象方法時,會包含在class的定義內,如
publicclassCoordinate{
publicfloatX{get;set;}
publicfloatY{get;set;}
//列印座標
publicvoidGetCoordinate(){
Console.WriteLine("("+this.X+","+this.Y+")");
}
}
在go語言中,對象方法在結構體定義的外部添加
typeCoordinatestruct{
X,Yfloat32
}
//列印座標
func(coo*Coordinate)GetCoordinate(){
fmt.Printf("(%.2f,%.2f)\n",coo.X,coo.Y)
return
}
其中,func關鍵字後面的(coo*Coordinate),表示該函數傳入一個指向Coordinate的指標,可通過指標變數coo來操作結構體的值。
幾種結構體初始化
一、按原始欄位順序通過建立結構體
packagemain
import(
"fmt"
)
funcmain(){
p0:=Coordinate{1,2}
p0.GetCoordinate()
}
輸出:(1.00,2.00),其中X=1,Y=2
二、按照自訂欄位順序進行初始化
packagemain
import(
"fmt"
)
funcmain(){
p0:=Coordinate{Y:1,X:2}
p0.GetCoordinate()
}
輸出:(2.00,1.00),其中X=2,Y=1
三、通過new函數建立
packagemain
import(
"fmt"
)
funcmain(){
//給該結構體p2變數分配記憶體,它返回指向已指派記憶體的指標
p0:=new(Coordinate)
p0.X=1
p0.Y=2
p0.GetCoordinate()
}
輸出:(1.00,2.00),其中X=1,Y=2
其中p0:=new(Coordinate)等價於以下寫法
p3:=&Coordinate{X:1,Y:2}
p3:=&Coordinate{1,2}
比較三種建立方式
其中,第一種與第二種,p0均為一個類型為Coordinate的執行個體,而第三種p0為一個指向Coordinate的指標,相當於varp0*Coordinate=new(Coordinate)
一般在進行例如typeTstruct{a,bint}的結構體定義之後
習慣使用t:=new(T)給該結構體變數分配記憶體,它返回指向已指派記憶體的指標。變數t是一個指向T的指標,此時結構體欄位的值是它們所屬類型的零值。
聲明vartT也會給t分配記憶體,並零值化記憶體,但是這個時候t是類型T。在這兩種方式中,t通常被稱做類型T的一個執行個體(instance)或對象(Object)。vart*T=new(T)等價於t:=new(T)。
通過程式碼分析以上結論
packagemain
import(
"fmt"
)
funcmain(){
p0:=Coordinate{1,2}
//給該結構體p2變數分配記憶體,它返回指向已指派記憶體的指標
p2:=new(Coordinate)
p2.X=1
p2.Y=2
p3:=&Coordinate{X:1,Y:2}
p4:=&Coordinate{1,2}
fmt.Println("-------輸出p0-------")
fmt.Printf("%v\n%T\n",p0,p0)
fmt.Println("-------輸出p2-------")
fmt.Printf("%v\n%T\n",p2,p2)
fmt.Println("-------輸出p3-------")
fmt.Printf("%v\n%T\n",p3,p3)
fmt.Println("-------輸出p4-------")
fmt.Printf("%v\n%T\n",p4,p4)
}
輸出:
-------輸出p0-------
{12}
Coordinate
-------輸出p2-------
&{12}
*Coordinate
-------輸出p3-------
&{12}
*Coordinate
-------輸出p4-------
&{12}
*Coordinate
可以看出來,p2,p3,p4均為一個指標變數
添加值拷貝的對象方法
剛才說到了,添加一個對象方法,可以通過func(t*T)functionname()來建立,其中t為一個指標變數。我們也可以通過值拷貝的方式,添加一個對象方法,文法為func(tT)functionname()
packagemain
import(
"fmt"
)
typeCoordinatestruct{
X,Yfloat32
}
func(coo*Coordinate)GetCoordinate(){
fmt.Printf("(%.2f,%.2f)\n",coo.X,coo.Y)
return
}
//值拷貝對象方法
func(cooCoordinate)SetPosition01(afloat32,bfloat32){
coo.X=a
coo.Y=b
}
//指標變數對象方法
func(coo*Coordinate)SetPosition02(afloat32,bfloat32){
coo.X=a
coo.Y=b
}
funcmain(){
p0:=Coordinate{1,2}
fmt.Print("SetPosition02調用前:")
p0.GetCoordinate()
p0.SetPosition02(0,0)
fmt.Print("SetPosition02調用後:")
p0.GetCoordinate()
}
輸出:
SetPosition01調用前:(1.00,2.00)
SetPosition01調用後:(1.00,2.00)
SetPosition02調用前:(1.00,2.00)
SetPosition02調用後:(0.00,0.00)
從程式輸出中可以看出,調用SetPosition01方法,發生了值拷貝,即使在方法內改變了coo的值,外部的p0的值沒有被改變。而SetPosition02方法中,coo為指向p0地址的指標,由於是通過指標變數修改了X,Y的值,所以調用完畢後,外部p0的值會被修改為(0,0)
匿名結構體
packagemain
import(
"fmt"
)
funcmain(){
p_3d:=struct{
X,Y,Zfloat32
}{1,2,3}
fmt.Println("-------輸出p_3d-------")
fmt.Printf("%v\n%T\n",p_3d,p_3d)
}
輸出:
-------輸出p_3d-------
{123}
struct{Xfloat32;Yfloat32;Zfloat32}
p_3d為一個包含X,Y,Z三個變數的匿名結構體
golang建構函式?
在Go語言中沒有建構函式的概念,對象的建立通常交由一個全域的建立函數來完成,以NewXXX來命名,表示“建構函式”:
這一切非常自然,開發人員也不需要分析在使用了new之後到底背後發生了多少事情。在Go語言中,一切要發生的事情都直接可以看到。
funcNewRect(x,y,width,heightfloat64)*Rect{
return&Rect{x,y,width,height}
}
變數、方法可見度
Go語言對關鍵字的增加非常吝嗇,其中沒有private、protected、public這樣的關鍵字。要使某個符號對其他包(package)可見(即可以訪問),需要將該符號定義為以大寫字母開頭,如:
typeRectstruct{
X,Yfloat64
Width,Heightfloat64
}
這樣,Rect類型的成員變數就全部被匯出了,可以被所有其他引用了Rect所在包的代碼訪問到。成員方法的可訪問性遵循同樣的規則,例如:
func(r*Rect)area()float64{
returnr.Width*r.Height
}
這樣,Rect的area()方法只能在該類型所在的包內使用。
需要注意的一點是,Go語言中符號的可訪問性是包一級的而不是類型一級的。在上面的例子中,儘管area()是Rect的內部方法,但同一個包中的其他類型也都可以訪問到它。這樣的可訪問性控制很粗曠,很特別,但是非常實用。如果Go語言符號的可訪問性是類型一級的,少不了還要加上friend這樣的關鍵字,以表示兩個類是朋友關係,可以訪問彼此的私人成員。