前言
之前和大家分享了容器以及相關的基礎語法,以及函數,相信如果大家有接觸過C++或者java的朋友都曉得面向對象,其實在GO語言中也存在面向對象,但是還是比較簡單的,下面我們來看一下GO語言的“面向對象”。
面向對象
結構體的定義
其實在GO語言中並不能准確得說是面向對象,go語言其實是面向接口函數編程的語言。為什么要說成GO語言的面向對象,其實也是部分特性類似於面向對象。GO語言中的面向對象還是比較簡單的,GO語言僅支持封裝,不支持多態和繼承。語言沒有class,只有struct。
結構體本質上也是一種數據類型,它是將0個或者多個任意類型的命名變量組合在一起的聚合數據類型,其中每個變量都叫做結構體的成員。看下結構體的定義,具體如下
type Employee struct {
ID int //id編號
Name string //名稱
Address string //地址
Position string //職位
}
func main() {
var jack Employee
jack.Name = "jack"
jack.Address = "shanghai"
fmt.Println(jack.Name)
}
以上我們就定義一個員工的結構體,這個結構體包含了名稱、地址、職位等一些屬性。在main函數中,我們創建了一個Employee類型的jack,並且給予其初始化了值。上述比較簡單地,咱們可以直接用“.”的方式進行對結構體變量進行賦值以及取值,當然咱們也可以獲取成員變量的地址,然后通過指針來訪問它。如下:
func main() {
var jack Employee
jack.Name = "jack"
jack.Address = "shanghai"
jack.Position = "Engineer"
//fmt.Println(jack.Name)
position := &jack.Position
*position = "Senior" + *position
fmt.Println(*position)
}
顯然這里的jack被升值了,變成了高級工程師。
咱們的點號其實也可以直接用在結構體的指針上,如下
func main() {
var jack Employee
jack.Name = "jack"
jack.Address = "shanghai"
jack.Position = "Engineer"
//fmt.Println(jack.Name)
position := &jack.Position
*position = "Senior" + *position
//fmt.Println(*position)
var employeeA *Employee = &jack
employeeA.Position = "Super" + employeeA.Position
fmt.Println(employeeA.Position)
}
還沒理解透徹指針的小伙伴可以會有點懵,后面老貓還是專門把指針說一下。上面的那個步驟,我們只是獲取了jack的職位並通過指針將其重新賦值升級,那么下面,其實咱們就定義了一個Employee的指針,並且這個指針指向的是jack這個結構體,那么針對我們的employeeA這個員工指針就能獲取其結構體中所有的屬性,並且將其重新賦值。
當然我們甚至可以定義指針類型的結構體函數,當然,其返回值必須是某個結構體的地址,具體定義如下:
func EmployeeByID(id int) *Employee {
var json Employee
json.ID = id
json.Name = "json"
json.Address = "beijing"
json.Position = "Engineer"
return &json
}
上述,咱們主要介紹了結構體以及指針的相關的用法,那么關於結構體的話還有哪些注意點呢?
- 成員變量的順序對於結構體同一性很重要,如果我們將上面的Employee結構體的屬性進行順序顛倒調換,那么我們就說定義了另外一個不同類型的結構體。
- 關於GO結構中定義變量的大小寫,大家可以看到,老貓上述定義的都是以大寫字母開頭的,因為只有以大寫字母開頭定義的屬性,才能夠被外圍訪問。大家可以手動敲一下代碼體驗一下。這個也是GO最主要的訪問控制機制。
- 結構體類型不可以定義一個擁有相同結構體類型s的成員變量,也就是一個聚合類型不可以包含它自己。
這個是什么意思呢?咱們來看一下例子!
上圖我們可以看到,如果結構體中套有自身是會報編譯錯誤的。但是Employee中可以定義個S的指針類型。例如下面則是OK的
type Employee struct {
ID int
Name string
Address string
Position *Employee
}
所以咱們就可以利用這種形式來做遞歸結構的定義,例如鏈表或者樹的定義咱們就可以這么來定義
type tree struct {
value int
left,right *tree
}
結構體的字面量
這里說的字面量就是結構體中的值,我們結構體類型中的值可以通過結構體字面量來進行設置。如下,有兩種結構體字面量。
第一種
type Point struct {X,Y int}
p:=Point{1,2}
這種方式要求按照正常順序為每個成員變量進行賦值,很顯然,如果結構體比較簡單的時候無所謂,但是一旦結構體之后隨着業務的演化變得相當復雜的時候,代碼的可維護性就變得相當差了。
第二種
type Point struct {X,Y int}
p:=Point{X:1,Y:2}
這種方式顯然會比較清晰一些,但是需要注意的是如果其中某個成員變量沒有指定的值的話,那么其值默認就為零值。由於指定了成員變量的名字,在這種方式中相當於第一種而言,這里的順序就無所謂了。
結構體的比較
如果結構體的所有成員變量都可以比較,那么這個結構體就是可以比較的,兩個結構體的比較直接使用==或者!=即可。
p:=Point{1,2}
q:=Point{2,1}
e:=Point{1,2}
fmt.Println(q.x == p.x) //成員函數比較 false
fmt.Println(p == q) //整體比較 false
fmt.Println(p == e) // true
在面向對象語言中,例如java,在我們比較兩個對象值的時候需要去比較兩個對象的hash值,甚至需要重寫equals方法,我們在這里看到的go語言的結構體對象的比較就很簡單明了了。這里不多贅述,還是希望大家能夠多寫寫。
結構體的嵌套機制
結構體的嵌套機制可以讓我們將一個命名結構體當做另一個結構體類型的匿名成員來使用。這句話可能有點不好理解,我們還是來直接看一下例子。
首先咱們來定義一個圓,圓的話包含了圓心的坐標以及相關的半徑,由此,咱們可以抽象出如下代碼
type Circle struct {
X,Y,Radius int
}
在我們的日常生活中,輪子也是圓形的,輪子可能多一些條幅數,由此,咱們輪子也可以抽象一下
type Wheel struct {
X,Y,Radius,Spokes int
}
看到上述兩個,咱們其實可以發現這兩個結構體中有挺多相同的成員變量,那咱們是不是可以再度抽象一下,於是咱們就抽象成了如下:
type Circle struct {
Center Point
Radius int
}
type Wheel struct {
Circle Circle
Spokes int
}
這樣,咱們就會發現整個程序看起來變得更加清晰。其實這也是更好地說明了結構體說白了也是一種特殊的數據類型而已。
寫在最后
本篇中其實和大家粗略分享了結構體的相關知識,有java相關面向對象語言經驗的小伙伴會發現,結構體和面向對象語言中的類比較相像,但是GO語言中的結構體的用法相比之下會簡單得多。
我是老貓,更多內容,歡迎大家搜索關注老貓的公眾號“程序員老貓”。