Go 面向對象式編程

『就要學習 Go 語言』系列 -- 第 25 篇分享好文golang

Go 語言沒有對象的概念,可是 struct 類型有着和對象相似的特性。struct 類型能夠定義本身的屬性和方法。這篇文章咱們來總結下 Go 語言中關於 「繼承」 和多態的概念。函數

嵌入類型

嵌入類型是指將已有的類型直接聲明在新的結構類型裏。不像 Java、C++ 等語言,Go 語言沒有繼承,可是能夠經過組合的方式實現代碼的複用。post

type User struct {
	Name string
	Email string
}

type Admin struct {
	User
	Level string
}

func (u *User) Speak()  {
	fmt.Println("I am user",u.Name)
}
複製代碼

上面的代碼定義了兩個結構體 User 和 Admin,Admin 有一個匿名成員 User,由於是匿名,因此類型即名稱。將 User 嵌入 Admin,Admin 是被嵌入的類型,也稱外部類型,User 是內部類型。Speak() 是 User 的方法。學習

經過嵌入,內部類型的屬性、方法,能夠爲外部類型全部,就好像是外部類型本身的同樣。此外,外部類型還能夠定義本身的屬性和方法,甚至能夠定義與內部相同的方法,這樣內部類型的方法就會被「屏蔽」。spa

admin := Admin{
		User:User{
			Name:"Jack",
			Email:"Jack@gmail.com",
		},
		Level:"admin",
	}
	// 內部類型的方法也被提高到外部類型
	admin.Speak()   // 方式一
	// 直接訪問內部類型的方法
	admin.User.Speak()  // 方式二
複製代碼

輸出:.net

I am user Jack
I am user Jack
複製代碼

給 Admin 定義本身的 Speak() 方法:指針

func (a *Admin) Speak()  {
	fmt.Println("I am admin",a.Name)
}

func main()  {
	admin := Admin{
		User:User{
			Name:"Jack",
			Email:"Jack@gmail.com",
		},
		Level:"admin",
	}
	admin.Speak()   // 方式一
	admin.User.Speak()  // 方式二
}
複製代碼

輸出:code

I am admin Jack
I am user Jack
複製代碼

能夠看到,Admin 定義了本身的 Speak() 方法時,會自動調用本身的方法,而屏蔽內部類型的方法。對於屬性也是同樣的狀況。
查看完整代碼cdn

另外,更重要的是,若是內部類型實現接口 A,也能夠認爲外部類型也實現了接口 A。對象

type Speaker interface {
	Speak()   // 方法
}

func gotoSpeak(s Speaker) {
	s.Speak()
}
複製代碼

定義了 Speaker 接口,任意類型若是實現了接口中定義的所有方法,就認爲該類型實現了接口。例如,上面定義的 User 類型,就實現了接口 Speaker。gotoSpeak() 函數是接收 Speaker 接口類型的參數,任何實現了 Speaker 接口的類型均可以調用該函數。

admin := Admin{
		User: User{
			Name:  "Jack",
			Email: "Jack@gmail.com",
		},
		Level: "admin",
	}
	gotoSpeak(&admin)
複製代碼

輸出:

I am user Jack
複製代碼

注意,關鍵點來了,調用 gotoSpeak() 時傳的參數是 admin 的地址,類型是 *Admin,不能傳 Admin 類型的值。從上篇文章咱們知道,Admin 類型的方法集中不包括 Speak() 方法,也就是說 Admin 類型沒有實現 Speaker 接口。

結合上篇關於類型方法集,對於嵌入類型的內部類型方法的提高能夠總結下。假設外部結構體類型是 S,內部類型是 T,則關於內部類型的方法提高以下規則:

  1. T 嵌入 S,外部類型 S 能夠經過值類型或指針類型調用內部類型 T 的值方法;
  2. T 嵌入 S,外部類型 S 只能經過指針類型調用內部類型 T 的指針方法;
  3. *T 嵌入 S,外部類型 S 能夠經過值類型和指針類型調用內部類型 T 的值方法和指針方法;

上面的三條規則能夠總結成一句話:無論是 T 嵌入 S,仍是 *T 嵌入 S,外部類型 S 惟獨經過值類型不能調用內部類型 T 的指針方法外,其餘狀況下內部類型 T 的方法均可以得到提高,便可被外部類型 S 訪問 。

前兩點其實很好理解,第三點是經過指針方式組合,其實就是在外部類型初始化的時候,取得內部類型的指針。其餘規則與非指針方式組合一致。

type Admin struct {
	*User            // 經過指針方式組合
	Level string
}

func main() {
	admin := Admin{
		User: &User{
			Name:  "Jack",
			Email: "Jack@gmail.com",
		},
		Level: "admin",
	}
	gotoSpeak(&admin)
	gotoSpeak(admin)
}
複製代碼

輸出:

I am user Jack
I am user Jack
複製代碼

多態

其實上面的例子已經給出多態的例子了,這個給你們提一下。在 Go 語言中,每種類型都是不一樣的,但不一樣的類型能夠實現同一接口,將它們綁定在同一接口上,用做函數或者放的輸入(輸出)參數。例如上面的 User 類型和 Admin 類型就是經過 Speaker 接口創建了關係。

深刻閱讀:
1.教女友寫方法(續)
2.Polymorphism - OOP in Go
3.Is Go An Object Oriented Language?
4.《Go 語言實戰》5.4 5.5 節


(全文完)

原創文章,若需轉載請註明出處!
歡迎掃碼關注公衆號「Golang來啦」或者移步 seekload.net ,查看更多精彩文章。

給你準備了學習 Go 語言相關書籍,公號後臺回覆【電子書】領取!

公衆號二維碼
相關文章
相關標籤/搜索