Golang學習筆記1-組合和介面
主要內容
1.組合
2.介面
3.常見問題
4.總結
1.組合
Golang中沒有OOP中繼承的概念,取而代之的是組合,同樣起到屬性和方法繼承 的作用,特別是匿名組合,其表現形式和繼承的行為類似,但是卻有很多不同的地方,相對於繼承、多型這些典型的OOP思想,組合顯得更加簡潔靈活。
1.1.一般組合
type Wifi struct {
Name string
}
type Phone struct {
WifiMdl Wifi
}
func Test_Group(t *testing.T) {
phone := Phone{}
phone.WifiMdl = Wifi{
Name: "..."
}
...
}
這是一個簡單的示例,直接將其他的型別作為自己的內部成員;那麼新型別的例項就可以直接對此成員進行賦值和初始化;
1.2.匿名組合
當我們嵌入一個型別,這個型別的方法就變成了外部型別的方法,但是當它被呼叫時,方法的接受者是內部型別(嵌入型別),而非外部型別。— Effective Go
理解了這句話也就理解匿名組合:簡單地可以理解為,內部型別的成員名可理解為內部型別名,在外部型別未定義相同名稱的屬性和方法時,內部方法可提升為外部型別的屬性和方法,但實際上,屬性和方法的所有者和呼叫的接受仍然為內部型別
// 內部型別
type People struct {
Name string
}
func (p *People) GetName() {
log.Print(p.Name)
}
// 外部型別
type Man struct {
People
}
func Test_Group(t *testing.T) {
man := Man{}
man.Name = "villare"
man.GetName()
}
匿名組合,即嵌入的型別沒有自己的屬性名,這種方式的組合和一般的組合最大的不同在於,外部型別的例項可以直接通過自身訪問內部型別的屬性和方法,“看上去外部型別作為屬性和方法的所有者”,形式上與繼承的類似;
特別的,如果內部型別也為指標
// 內部型別
type People struct {
Name string
}
func (p *People) GetName() {
log.Print(p.Name)
}
// 外部型別
type Man struct {
*People
}
func Test_Group(t *testing.T) {
man := Man{}
//以下程式碼報錯(invalid memory address or nil pointer dereference...)
//先初始化 man := Man{&People{},}
man.Name = "villare"
man.GetName()
}
指標型別的匿名組合在直接呼叫內部型別的方法時,會出現地址未分配的錯誤;可見,指標型別的匿名組合需要在外部型別初始化的同時進行手動初始化,不會自動初始化內部型別;
由此可見,匿名組合中內部方法的接受者實際上還是內部型別;
2.介面
2.1.介面的定義
type People interface {
Sex()
}
介面的定義和其他語言有相似之處,直接定義介面方法,不過不支援靜態成員和預設方法的定義形式;
由於golang介面的實現形式使得它沒必要支援這些特性
2.2.介面的實現
type People interface {
Sex()
}
type Man struct {}
func (m *Man) Sex() {
log.Print("Man")
}
type Woman struct {}
func (m *Woman) Sex() {
log.Print("Woman")
}
func Test_Inter(t *testing.T) {
var t1 People
t1 = new(Man)
t1.Sex()
t1 = new(Woman)
t1.Sex()
}
golang的介面實現遵循一個原則:如果一個型別實現了介面定義的所有方法,那麼就認為此型別實現了這個介面
golang中方法不能重名,不支援過載
golang不使用介面指標,直接使用介面型別,注意和java、c++的區別
3.常見問題
3.1.組合中的訪問控制
golang預設的訪問控制規則是:首字母大寫(Public),首字母小寫(private)
對於組合來說,內部型別的私有成員在外部型別中可見,但是仍然為私有成員(不可通過例項直接訪問)
3.2.嵌入衝突
type Time struct {
Name string
}
// 正確示例
type UTC struct {
time.Time
T2 Time
}
// 錯誤示例
type UTC struct {
time.Time
Time
}
在嵌入多個型別時,保證成員名不同,匿名組合的成員名為型別名稱
即使不同包路徑不同,只要型別名相同,匿名組合均有衝突
3.2.匿名組合的衝突
注意一點,成員和方法的優先順序:
外部型別 > 內部型別 > 內部型別的內部型別 > …
如果出現歧義(Ambiguous),通過加型別名的形式呼叫,明確一點“屬性和方法的所有者和呼叫者均屬於內部方法”。
關注下面一個例項
type People struct {
Name string
}
func (p *People) GetSex() {
log.Print("Man Or Woman")
}
type Man struct {
People
}
func (p *Man) GetSex() {
log.Print("Man")
}
type Child struct {
Man
}
func Test_Group(t *testing.T) {
cld := Child{}
cld.GetSex()
cld.Man.GetSex()
cld.People.GetSex()
}
//輸出為
Man
Man
Man Or Woman
4.總結
總的來說,golang組合和介面的兩個概念還是相對比較好理解的,規則也更加契合主觀認知,減少了理解和學習的成本;但在簡潔的同時也並沒有減弱golang語言本身的表達和設計能力;