【Go語言入門系列】(八)Go語言是不是面向對象語言?


【Go語言入門系列】前面的文章:

1. Go是面向對象的語言嗎?

【Go語言入門系列】(七)如何使用Go的方法?這一文中已經介紹了方法的概念,但這個方法實際上並不是面向對象中的方法。方法實際上是用戶給其定義的類型的增加的新行為,實際上也是個函數。

關於這個問題,官方文檔中有回答:

Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. There are also ways to embed types in other types to provide something analogous—but not identical—to subclassing. Moreover, methods in Go are more general than in C++ or Java: they can be defined for any sort of data, even built-in types such as plain, “unboxed” integers. They are not restricted to structs (classes).

Also, the lack of a type hierarchy makes “objects” in Go feel much more lightweight than in languages such as C++ or Java.

既是也不是。盡管Go擁有類型和方法,也允許面向對象風格的編程,但它沒有類型層級。 在Go中“接口”的概念提供了不同的方法,我們相信它易於使用且在某些方面更通用。 也有一些在其它類型中嵌入類型的方法,來提供類似(而非完全相同)的東西進行子類化。 此外,Go中的方法比C++或Java中的更通用:它們可被定義為任何種類的數據。 甚至是像普通的“未裝箱”整數這樣的內建類型。它們並不受結構(類)的限制。

此外,類型層級的缺失也使Go中的“對象”感覺起來比C++或Java的更輕量級

有了這個回答,下面介紹的“繼承”和“重寫”的概念並不是嚴格的面向對象中的繼承和重寫的概念。這里只借用這兩個名詞來表示Go的兩種特性。

2.“繼承”

在面向對象中,繼承是子類和父類之間的關系,子類會繼承父類的公有成員變量和成員方法。

前面提到過,Go允許面向對象風格的編程。那Go如何“繼承”呢?

2.1.“繼承”字段

【Go語言入門系列】(五)指針和結構體的使用這一文中,介紹了匿名字段(也叫嵌入字段):

package main

import "fmt"

type people struct {
	name string
	age int
}

type student struct {
	people
	school string
}

func (s student) say() {
	fmt.Printf("我是%s,今年%d歲了,在%s上學。", s.name, s.age, s.school)
}

func main() {
	stu := student{people{"行小觀", 1}, "陽光小學"}
	stu.say()
}

運行:

我是行小觀,今年1歲了,在陽光小學上學

結構體student中有匿名字段people,所以student就有了peoplenameage字段。當匿名字段是一個結構體時,那么該結構體的全部字段都會被引入當前的結構體中。這是不是很像面向對象中的繼承?子類繼承父類的公有成員變量。

考慮下面一個問題,如果studentpeople中都有name字段,那么訪問時,Go語言是如何處理的?

package main

import "fmt"

type people struct {
	name string //人名
	age int
}

type student struct {
	people
	name string //學生名
	school string
}

func main() {
	stu := student{people{"李二狗", 1}, "李向前", "陽光學校"}
	fmt.Println(stu.name) //李向前
	fmt.Println(stu.people.name) //李二狗
}

此時就出現了字段沖突,Go會先訪問外層的字段。比如,stu.name李向前(外層),stu.people.name李二狗(內層)。

2.2.“繼承”方法

我們通過接收者把函數綁定到結構體類型上,這樣的函數稱為方法,方法就在概念上屬於了接收者對應的結構體。

上面通過結構體中匿名字段實現了“繼承”字段的效果,對於方法來說,同樣可以。下面是一個實例:

package main

import "fmt"

type people struct {
	name string
	age int
}

type student struct {
	people
	school string
}

type programmer struct {
	people
	language string
}

func (p people) say() {
	fmt.Printf("我是%s,今年%d歲了,和我一起學習Go語言吧!\n", p.name, p.age)
}

func main() {
	stu := student{people{"行小觀", 1}, "陽光小學"}
	stu.say()
	prom := programmer{people{"張三", 1}, "藍天建築有限公司"}
	prom.say()
}

運行:

我是行小觀,今年1歲了,和我一起學習Go語言吧!
我是張三,今年1歲了,和我一起學習Go語言吧!

say()方法的接收者是people類型,而結構體studentprogrammer中都有匿名字段people,所以stuprom都能調用say()方法。這是不是很像面向對象語言中子類繼承父類的公有方法?

3. “重寫”

3.1. “重寫”字段

前面已經介紹了如果結構體和作為其字段的結構體的字段沖突了如何處理。有了這個特性,我們就可以“重寫”字段。

package main

import "fmt"

type people struct {
	name string //乳名
	age int
}

type student struct {
	people
	name string //大名
	school string
}

func (s student) say() {
	fmt.Printf("我是%s,今年%d歲了,和我一起學習Go語言吧!\n", s.name, s.age)
}

func main() {
	stu := student{people{"李二狗", 1}, "李向前","陽光小學"}
	stu.say()
}

運行:

我是李向前,今年1歲了,和我一起學習Go語言吧!

李二狗是乳名,李向前才是大名。自我介紹時說的是大名李向前。如果需要乳名,則使用stu.people.name

3.2.“重寫”方法

下面是一個實例:

package main

import "fmt"

type people struct {
	name string
	age int
}

type student struct {
	people
	school string
}

type programmer struct {
	people
	language string
}

func (p people) say() { //people的say方法
	fmt.Printf("我是%s,今年%d歲了,和我一起學習Go語言吧!\n", p.name, p.age)
}

func (s student) say() { //student重寫people的say方法
	fmt.Printf("我是%s,是個學生,今年%d歲了,我在%s上學!\n", s.name, s.age, s.school)
}

func (p programmer) say() { //programmer重寫people的say方法
	fmt.Printf("我是%s,是個程序員,今年%d歲了,我使用%s語言!\n", p.name, p.age, p.language)
}

func main() {
	stu := student{people{"李向前", 1}, "陽光小學"}
	stu.say()
	prmger := programmer{people{"張三", 1}, "Go"}
	prmger.say()
}

運行:

我是李向前,是個學生,今年1歲了,我在陽光小學上學!
我是張三,是個程序員,今年1歲了,我使用Go語言!

studentprogrammer“繼承”了peoplesay()方法,但是不合適,於是各自“重寫”了say()方法。

看到這里,你就理解了官方文檔中的那兩句話是什么意思了。

“盡管Go擁有類型和方法,也允許面向對象風格的編程,但它沒有類型層級”

“類型層級的缺失也使Go中的“對象”感覺起來比C++或Java的更輕量級”

作者簡介

我是行小觀,我會在公眾號『行人觀學』中持續更新Java、Go、數據結構和算法、計算機基礎等相關文章。


本文章已收錄進系列文章「Go語言入門系列」,本系列從Go語言基礎開始介紹,適合從零開始的初學者。


歡迎關注,我們一起踏上編程的行程。

如有錯誤,還請指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM