咱們知道,在 Go 語言中沒有類(Class)的概念,但這並不意味着 Go 語言不支持面向對象編程,畢竟面向對象只是一種編程思想。git
讓咱們回憶一下面向對象的三大基本特徵:github
咱們一塊兒來看看 Go 語言是如何在沒有類(Class)的狀況下實現這三大特徵的。編程
在 Go 語言中可使用結構體(Structs)對屬性進行封裝,結構體就像是類的一種簡化形式。數組
例如,咱們要定義一個矩形,每一個矩形都有長和寬,咱們能夠這樣進行封裝:編程語言
type Rectangle struct {
Length int
Width int
}
複製代碼
既然有了「類」,你可能會問了,那「類」的方法在哪呢?ide
Go 語言中也有方法(Methods):Go 方法是做用在接收者(receiver)上的一個函數,接收者是某種類型的變量。所以方法是一種特殊類型的函數。函數
定義方法的格式以下:post
func (recv receiver_type) methodName(parameter_list) (return_value_list) { ... }
複製代碼
上文中咱們已經定義了一個矩形 Rectangle
,如今咱們要定義一個方法 Area()
來計算它的面積:ui
package main
import (
"fmt"
)
// 矩形結構體
type Rectangle struct {
Length int
Width int
}
// 計算矩形面積
func (r *Rectangle) Area() int {
return r.Length * r.Width
}
func main() {
r := Rectangle{4, 2}
// 調用 Area() 方法,計算面積
fmt.Println(r.Area())
}
複製代碼
上面的代碼片斷輸出結果爲 8。spa
咱們常會說一個類的屬性是公共的仍是私有的,在其餘編程語言中,咱們經常使用 public
與 private
關鍵字來表達這樣一種訪問權限。
在 Go 語言中沒有 public
、private
、protected
這樣的訪問控制修飾符,它是經過字母大小寫來控制可見性的。
若是定義的常量、變量、類型、接口、結構、函數等的名稱是大寫字母開頭,這表示它們能被其它包訪問或調用(至關於 public
);非大寫開頭就只能在包內使用(至關於 private
)。
當遇到只能在包內使用的未導出字段時,咱們又該如何訪問呢?
和其餘面嚮對象語言同樣,Go 語言也有實現 getter
和 setter
的方式:
setter
方法使用 Set
前綴getter
方法只使用成員名例如咱們如今有一個處於 person
包中的 Person
結構體:
package person
type Person struct {
firstName string
lastName string
}
複製代碼
咱們能夠看到,它的兩個成員變量都是非大寫字母開頭,只能在包內使用,如今咱們爲其中的 firstName
來定義 setter
與 getter
:
// 獲取 firstName
func (p *Person) FirstName() string {
return p.firstName
}
// 設置 firstName
func (p *Person) SetFirstName(newName string) {
p.firstName = newName
}
複製代碼
這樣一來,咱們就能夠在 main
包裏設置和獲取 firstName
的值了:
package main
import (
"fmt"
"./person"
)
func main() {
p := new(person.Person)
p.SetFirstName("firstName")
fmt.Println(p.FirstName())
}
/* Output: firstName */
複製代碼
在 Go 語言中沒有 extends
關鍵字,它使用在結構體中內嵌匿名類型的方法來實現繼承。
匿名類型:即這些類型沒有顯式的名字。
咱們定義一個 Engine
接口類型,一個 Car
結構體,讓 Car
結構體包含一個 Engine
類型的匿名字段:
type Engine interface {
Start()
Stop()
}
type Car struct {
Engine // 包含 Engine 類型的匿名字段
}
複製代碼
此時,匿名字段 Engine
上的方法「晉升」成爲了外層類型 Car
的方法。咱們能夠構建出以下代碼:
func (c *Car) GoToWorkIn() {
// get in car
c.Start()
// drive to work
c.Stop()
// get out of car
}
複製代碼
在面向對象中,多態的特徵爲:不一樣對象中同種行爲的不一樣實現方式。在 Go 語言中可使用接口實現這一特徵。
咱們先定義一個正方形 Square
和一個長方形 Rectangle
:
// 正方形
type Square struct {
side float32
}
// 長方形
type Rectangle struct {
length, width float32
}
複製代碼
而後,咱們但願能夠計算出這兩個幾何圖形的面積。但因爲他們的面積計算方式不一樣,咱們須要定義兩個不一樣的 Area()
方法。
因而,咱們能夠定義一個包含 Area()
方法的接口 Shaper
,讓 Square
和 Rectangle
都實現這個接口裏的 Area()
:
// 接口 Shaper
type Shaper interface {
Area() float32
}
// 計算正方形的面積
func (sq *Square) Area() float32 {
return sq.side * sq.side
}
// 計算長方形的面積
func (r *Rectangle) Area() float32 {
return r.length * r.width
}
複製代碼
咱們能夠在 main()
函數中這樣調用 Area()
:
func main() {
r := &Rectangle{10, 2}
q := &Square{10}
// 建立一個 Shaper 類型的數組
shapes := []Shaper{r, q}
// 迭代數組上的每個元素並調用 Area() 方法
for n, _ := range shapes {
fmt.Println("圖形數據: ", shapes[n])
fmt.Println("它的面積是: ", shapes[n].Area())
}
}
/*Output: 圖形數據: &{10 2} 它的面積是: 20 圖形數據: &{10} 它的面積是: 100 */
複製代碼
由以上代碼輸出結果可知:不一樣對象調用 Area()
方法產生了不一樣的結果,展示了多態的特徵。
getter
和 setter
的方式來訪問若是你以爲文章寫得不錯,請幫我兩個小忙:
原創不易,多多鼓勵~謝謝你們!