1. 程式人生 > 實用技巧 >golang學習筆記 --類與方法

golang學習筆記 --類與方法

1. 型別系統[類的宣告]

型別系統:

  • 一組基本型別構成的“基本型別集合”;
  • “基本型別集合”上定義的一系列組合、運算、轉換方法。

型別系統包括基礎型別(byte、int、bool、float等);複合型別(陣列、結構體、指標等);可以指向任何物件的型別(Any型別,類似Java的Object型別);值語義和引用語義;面向物件型別;介面。Go大多數型別為值語義,可以給任何型別新增方法(包括內建型別,不包括指標型別)。Any型別是空介面即interface{}。

2. 結構體

結構體[類屬性的宣告]

struct的功能類似Java的class,可實現巢狀組合(類似繼承的功能) struct實際上就是一種複合型別,只是對類中的屬性進行定義賦值,並沒有對方法進行定義,方法可以隨時定義繫結到該類的物件上,更具靈活性。可利用巢狀組合來實現類似繼承的功能避免程式碼重複。

type Rect struct{   //定義矩形類
  x,y float64       //型別只包含屬性,並沒有方法
  width,height float64
}
func (r *Rect) Area() float64{    //為Rect型別繫結Area的方法,*Rect為指標引用可以修改傳入引數的值
  return r.width*r.height         //方法歸屬於型別,不歸屬於具體的物件,宣告該型別的物件即可呼叫該型別的方法
}

  

3. 方法

1、為型別新增方法[類方法宣告],方法即為有接收者的函式 func (物件名 物件型別) 函式名(引數列表) (返回值列表) 可隨時為某個物件新增方法即為某個方法新增歸屬物件(receiver),以方法為中心 在Go語言中沒有隱藏的this指標,即顯示傳遞,形參即為this,

例如以下的形參為a。

type Integer int
func (a Integer) Less(b Integer) bool{  //表示a這個物件定義了Less這個方法,a可以為任意型別
  return a<b                           
}
//型別基於值傳遞,如果要修改值需要傳遞指標
func (a *Integer) Add(b Integer){
  *a+=b    //通過指標傳遞來改變值
}

  

4. 值語義和引用語義

值型別:b的修改並不會影響a的值

引用型別:b的修改會影響a的值

Go大多型別為值語義,包括基本型別:byte,int,string等;複合型別:陣列,結構體(struct),指標等

//2、值語義和引用語義
b=a
b.Modify()

//值型別
var a=[3]int{1,2,3}
b:=a
b[1]++
fmt.Println(a,b)   //a=[1,2,3]  b=[1,3,3]
//引用型別
a:=[3]int{1,2,3}
b:=&a              //b指向a,即為a的地址,對b指向的值改變實際上就是對a的改變(陣列本身就是一種地址指向)
b[1]++
fmt.Println(a,*b)  //a=[1,3,3]  b=[1,3,3]   //*b,取地址指向的值

  

初始化[例項化物件]

資料初始化的內建函式new()與make(),二者都是用來分配空間。區別如下:

5.1. new()

func new(Type) *Type
內建函式 new 分配空間。傳遞給new 函式的是一個型別,不是一個值。返回值是指向這個新分配的零值的指標

  

5.2. make()

func make(Type, size IntegerType) Type
內建函式 make 分配並且初始化 一個 slice, 或者 map 或者 chan 物件。 並且只能是這三種物件。 和 new 一樣,第一個引數是 型別,不是一個值。 但是make 的返回值就是這個型別(即使一個引用型別),而不是指標。 具體的返回值,依賴具體傳入的型別。

  

5.3. 示例

//建立例項
rect1:=new(Rect)   //new一個物件
rect2:=&Rect{}     //為賦值預設值,bool預設值為false,int預設為零值0,string預設為空字串
rect3:=&Rect{0,0,100,200}     //取地址並賦值,按宣告的變數順序依次賦值
rect4:=&Rect{width:100,height:200}    //按變數名賦值不按順序賦值
//建構函式:沒有構造引數的概念,通常由全域性的建立函式NewXXX來實現建構函式的功能
func NewRect(x,y,width,height float64) *Rect{
  return &Rect{x,y,width,height}     //利用指標來改變傳入引數的值達到類似構造引數的效果
}
//方法的過載,Go不支援方法的過載(函式同名,引數不同)
//v …interface{}表示引數不定的意思,其中v是slice型別,及宣告不定引數,可以傳入任意引數,實現類似方法的過載
func (poem *Poem) recite(v ...interface{}) {
    fmt.Println(v)
}
package main

import "fmt"

type Rect struct { //定義矩形類
	x, y          float64 //型別只包含屬性,並沒有方法
	width, height float64
}

func (r *Rect) Area() float64 { //為Rect型別繫結Area的方法,*Rect為指標引用可以修改傳入引數的值
	return r.width * r.height //方法歸屬於型別,不歸屬於具體的物件,宣告該型別的物件即可呼叫該型別的方法
}

func main() {
	rect1 := new(Rect)                      //new一個物件
	rect2 := &Rect{}                        //為賦值預設值,bool預設值為false,int預設為零值0,string預設為空字串
	rect3 := &Rect{0, 0, 100, 200}          //取地址並賦值,按宣告的變數順序依次賦值
	rect4 := &Rect{width: 100, height: 200} //按變數名賦值不按順序賦值
	fmt.Println(rect1.Area())
	fmt.Println(rect2.Area())
	fmt.Println(rect3.Area())
	fmt.Println(rect4.Area())

}

  

  

6. 匿名組合[繼承]

​ 組合,即方法代理,例如A包含B,即A通過訊息傳遞的形式代理了B的方法,而不需要重複寫B的方法。

​ 繼承是指這樣一種能力:它可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴充套件。繼承主要為了程式碼複用,繼承也可以擴充套件已存在的程式碼模組(類)。

​ 嚴格來講,繼承是“a kind of ”,即子類是父類的一種,例如student是person的一種;組合是“a part of”,即父類是子類中的一部分,例如眼睛是頭部的一部分。

//1、匿名組合的方式實現了類似Java繼承的功能,可以實現多繼承
type Base struct{
  Name string
}
func (base *Base) Foo(){...}    //Base的Foo()方法
func (base *Base) Bar(){...}    //Base的Bar()方法
type Foo struct{  
  Base                         //通過組合的方式聲明瞭基類,即繼承了基類
  ...
}
func (foo *Foo) Bar(){
  foo.Base.Bar()               //並改寫了基類的方法,該方法實現時先呼叫基類的Bar()方法
  ...                          //如果沒有改寫即為繼承,呼叫foo.Foo()和呼叫foo.Base.Foo()的作用的一樣的
}
//修改記憶體佈局
type Foo struct{
  ...   //其他成員資訊
  Base
}
//以指標方式組合
type Foo struct{
  *Base   //以指標方式派生,建立Foo例項時,需要外部提供一個Base類例項的指標
  ...
}
//名字衝突問題,組合內外如果出現名字重複問題,只會訪問到最外層,內層會被隱藏,不會報錯,即類似java中方法覆蓋/重寫。
type X struct{
  Name string
}
type Y struct{
  X             //Y.X.Name會被隱藏,內層會被隱藏
  Name string   //只會訪問到Y.Name,只會呼叫外層屬性
}
package main

import "fmt"

type X struct {
	Name string
}
type Y struct {
	X           //Y.X.Name會被隱藏,內層會被隱藏
	Name string //只會訪問到Y.Name,只會呼叫外層屬性
}

func main() {
	a := X{"Mary"}
	b := Y{a, "Tom"}
	fmt.Println(b.X.Name)
	fmt.Println(b.Name)

}

  輸出:

Mary

Tom

7. 可見性[封裝]

封裝,也就是把客觀事物封裝成抽象的類,並且類可以把自己的資料和方法只讓可信的類或者物件操作,對不可信的進行資訊隱藏。

封裝的本質或目的其實程式對資訊(資料)的控制力。封裝分為兩部分:該隱藏的隱藏,該暴露的暴露。封裝可以隱藏實現細節,使得程式碼模組化。

Go中用大寫字母開頭來表示public,可以包外訪問;小寫字母開頭來表示private,只能包內訪問;訪問性是包級別非型別級別,如果可訪問性是型別一致的,可以加friend關鍵字表示朋友關係可互相訪問彼此的私有成員(屬性和方法)

type Rect struct{
  X,Y float64
  Width,Height float64           //字母大寫開頭表示該屬性可以由包外訪問到
}
func (r *Rect) area() float64{   //字母小寫開頭表示該方法只能包內呼叫
  return r.Width*r.Height
}