Go語言學習:02-結構體與方法

結構體

結構體

定義

結構體定義的是數據,與C語言的結構體相同。git

結構體定義以下:github

type structTypeName struct {
   member definition;
   member definition;
   ...
   member definition;
}

結構體變量能夠有兩種聲明方式(使用KV方式時,能夠僅初始化部分紅員):函數

varName := structTypeName {value1, value2...valuen}
varName := structTypeName { key1: value1, key2: value2..., keyn: valuen}

還有另一種,經過new來建立特定對象:測試

varName := new (structTypeName)	// 使用默認值初始化

代碼範例:spa

type student struct {
	name	string
	age		int
	grage	int
}

func main() {
	s1 := student{"wang", 10, 2}
	s2 := new(student)
	// s3 := make(student)  	// cannot make type student

	s2.name = "li";
	s2.age = 11;

	fmt.Println(s1)
	fmt.Println(s2)
}

內嵌結構體

內嵌結構體是指將某個結構體,潛入到其餘的結構體中,相似於C語言中的struct內部定義struct。
Go語言中不能在struct內部嵌套,而是須要將內嵌的結構體獨立聲明。指針

範例,咱們須要在student結構體中內嵌other結構體:code

type other struct {		// 聲明other結構體
	addr string
}

type student struct {
	name	string
	age		int
	grage	int
	other				// 內嵌other結構體
}

若是名字沒有衝突,能夠直接使用內嵌結構體的字段對象

s1 := student{"wang", 10, 2, other{"JS"}}	// 初始化時須要帶着內嵌結構體一塊兒
s1.addr = "JJ"			// 直接使用內嵌結構體的成員變量名
s1.other.addr = "AA"	// 訪問特定類型的內嵌結構體成員變量名,名字衝突時使用

這裏順便將內嵌組合區分下,若是使用組合方式,則student的定義將會相似以下:繼承

type student struct {
	name	string
	age		int
	grage	int
	etc		other	// 組合other對象
}

方法

概念

在 Go 語言中,結構體與C語言的結構體等價,表示純數據,那麼Go是否支持面向對象呢?如何實如今結構體上實現方法呢?接口

實際上,Go的方法是做用在接收者上的一個函數,接收者是某種特定類型的變量,因此咱們能夠人爲方法是一種特殊類型的函數,Go的這種綁定與Lisp語言很是類似。

Go中方法接收者類型幾乎能夠是任何類型,任何類型均可以有方法,甚至能夠是函數類型。接收者不能是如下類型:

  1. 接口類型,接口是一個抽象定義,而方法倒是具體實現,實現沒法綁定在抽象定義上;
  2. 指針類型。

類型+方法構成了傳統面嚮對象語言中的一個,在 Go 中,類型的代碼和綁定在它上面的方法的代碼必須位於同一個包中,但能夠存在於不一樣的源文件中(這個也就限制了您不能給int類型增長方法)。

Go語言中不支持方法重載,即對於一個類型只能有一個給定名稱的方法,可是具備一樣名字的方法能夠在 2 個或多個不一樣的接收者類型上存在。

方法定義:

func (recv receiverType) methodName(paraList) (returnValueList) { ... }

範例:

func (a *denseMatrix) Add(b Matrix) Matrix
func (a *sparseMatrix) Add(b Matrix) Matrix

方法和函數的區別

上文說起到,方法是一種特殊的函數,實際上咱們能夠人爲僅僅是格式有差別。

例如對於變量rcv

  • 使用函數時,將變量做爲參數傳遞給函數,例如FuncCall(recv)
  • 使用方法時,就像是調用變量的方法,例如rcv.Method()

Go的這種方法與結構獨立的方式,很好的解決了耦合問題。

以指針或者值做爲接收者

以值爲接收者時,調用方法時會將接收者的值傳遞進去,因此在方法內部修改接收者時,實際上修改的時傳遞進來的接收者副本,並不會影響接收者自己。

以指針爲接收者時,在方法內部修改接收者數據時,直接做用在接收者上。

之前面的student爲例,追加2個方法:

func (s *student) funcPtr() {
	s.name = "ptrName"
	fmt.Printf("funcPtr: Change student name to ptrName\n")
}

func (s student) funcObj() {
	s.name = "objName"
	fmt.Printf("funcObj: Change student name to objName\n")
}

測試代碼:

s1 := student{"wang", 10, 2, other{"JS"}}

s1.funcObj()
fmt.Println(s1)

s1.funcPtr()
fmt.Println(s1)

輸出結果:

funcObj: Change student name to objName
{wang 10 2 {JS}}
funcPtr: Change student name to ptrName
{ptrName 10 2 {JS}}

結論:使用指針方式傳遞時,能夠修改接收者的數據。

內嵌類型的方法

內嵌類型的綁定方法後,外層結構至關於自動擁有了該方法。

範例,內嵌類型Point綁定了Abs方法,此時NamedPoint類型將自動擁有該方法:

type Point struct {
	x, y float64
}

func (p *Point) Abs() float64 {
	return math.Sqrt(p.x*p.x + p.y*p.y)
}

type NamedPoint struct {
	Point
	name string
}

測試代碼:

n := NamedPoint{Point{3, 4}, "Pythagoras"}
fmt.Println(n.Abs()) // 輸出「5」

若是您在外層類中實現了同名方法,那麼外層類型的方法將覆蓋內層類型的方法

若是一個類型包含了多個子類型,就至關於該類型繼承了多個子類型的方法,等同於C++中的多重繼承。

方法調用規則

Go語言約定以下方法調用規則:

  • 類型 T 的可調用方法集包含接受者爲 *T 或 T 的全部方法集
  • 類型 *T 的可調用方法集包含接受者爲 *T 的全部方法
  • 類型 *T 的可調用方法集不包含接受者爲 T 的方法

也就是,方法應綁定到*T的類型上

參考

參考書籍:https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/directory.md

相關文章
相關標籤/搜索