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).
在接下來的教程中,咱們將討論如何使用 Go實現面向對象的編程概念。與其餘面向對象的語言(如Java)相比,它們中的一些在實現上有很大不一樣。編程
<!-- more -->併發
Go不提供類,但它確實提供告終構。能夠在結構上添加方法。這提供了將數據和方法捆綁在一塊兒的行爲,相似於類。app
讓咱們立刻開始一個例子,以便更好地理解。編程語言
咱們將在此示例中建立一個自定義包,由於它有助於更好地理解結構如何成爲類的有效替代。ide
在 Go工做區內建立一個文件夾並命名 oop
。在 oop
裏面建立一個子文件夾 employee
。在 employee
文件夾中,建立一個名爲 employee.go. 的文件,文件夾結構看起來像,函數
workspacepath -> oop -> employee -> employee.go
employee.go 內容:oop
package employee import ( "fmt" ) type Employee struct { FirstName string LastName string TotalLeaves int LeavesTaken int } func (e Employee) LeavesRemaining() { fmt.Printf("%s %s has %d leaves remaining", e.FirstName, e.LastName, (e.TotalLeaves - e.LeavesTaken)) }
在上面的程序中,第一行指定此文件屬於 employee包。Employee結構在第 7行號中聲明。在第 14 行將一個名爲的方法LeavesRemaining
添加到Employee的結構中。這將計算並顯示員工剩餘的離職數量。如今咱們有一個結構和一個方法,它運行在一個相似於類的結構上。ui
在oop
文件夾中建立一個名爲main.go
的文件。如今文件夾結構看起來像,spa
workspacepath -> oop -> employee -> employee.go workspacepath -> oop -> main.go
main.go 內容以下:
package main import "oop/employee" func main() { e := employee.Employee { FirstName: "Sam", LastName: "Adolf", TotalLeaves: 30, LeavesTaken: 20, } e.LeavesRemaining() }
咱們在第 3 行引入employee
包。在main.go
的第 12 行結構體 Employee
調用方法 LeavesRemaining()
。
此程序沒法在 playground 上運行,由於它有自定義程序包。若是你在本地運行這個程序,能夠在 workspacepath/bin/oop
文件夾下運行 go install oop
。這個命令將打印輸出:
Sam Adolf has 10 leaves remaining
咱們上面寫的程序看起來不錯,但它有一個小問題。讓咱們看看當咱們定義零值的Employee時會發生什麼。將內容更改成main.go
如下代碼,
package main import "oop/employee" func main() { var e employee.Employee e.LeavesRemaining() }
咱們所作的惟一改變是Employee在第6行建立一個零值。該程序將輸出,
has 0 leaves remaining
如您所見,使用零值建立的Employee變量不可用。它沒有有效的名字,姓氏,也沒有有效的休假詳情。
在像Java這樣的其餘OOP語言中,這個問題能夠經過使用構造函數來解決。可使用參數化構造函數建立有效對象。
Go不支持構造函數。若是一個類型的零值不可用,那麼程序員應該去掉導出,以防止其餘包訪問,而且還提供一個方法叫NewT(parameters),其初始化類型T與所需的值。Go中的一個約定是命名一個函數 NewT(parameters),它建立一個類型T。這將像一個構造函數。若是包只定義了一個類型,那麼Go中的一個約定就是命名這個函數New(parameters)而不是NewT(parameters)。
讓咱們對咱們編寫的程序進行更改,以便每次建立員工時均可以使用。
第一步是取消導出Employee結構並建立一個New()函數來建立Employee。
用 employee.go 如下內容替換代碼,
package employee import ( "fmt" ) type employee struct { firstName string lastName string totalLeaves int leavesTaken int } func New(firstName string, lastName string, totalLeave int, leavesTaken int) employee { e := employee {firstName, lastName, totalLeave, leavesTaken} return e } func (e employee) LeavesRemaining() { fmt.Printf("%s %s has %d leaves remaining", e.firstName, e.lastName, (e.totalLeaves - e.leavesTaken)) }
咱們在這裏作了一些重要的改變。咱們已將Employee struct 的起始字母改成小寫 e,即咱們已將 type Employee struct
更改成type employee struct
。經過這樣作,咱們已成功取消導出 employee 結構並阻止從其餘包訪問。除非特定須要導出它們,不然將未導出結構的全部字段都取消導出是一種很好的作法。因爲咱們不須要employee包外的任何結構的字段,所以咱們也取消了全部字段的輸出。
咱們已經更改了在LeavesRemaining()
方法中相應地字段名稱。
如今,因爲employee未導出,所以沒法從其餘包建立Employee類型的值。所以咱們在第14行 New() 提供了一個導出函數。將所需參數做爲輸入並返回新建立的員工。
該程序仍然須要進行更改以使其工做,可是讓咱們運行它來了解到目前爲止更改的效果。若是運行此程序,它將失敗並出現如下編譯錯誤,
go/src/constructor/main.go:6: undefined: employee.Employee
這是由於咱們已經取消導出Employee,所以編譯器會拋出此類型未定義的錯誤。完美。正是咱們想要的。如今沒有其餘包可以建立employee零值。咱們已成功阻止建立不可用的員工結構值。如今建立員工的惟一方法是使用該New功能。
用如下內容替換內容main.go,
package main import "oop/employee" func main() { e := employee.New("Sam", "Adolf", 30, 20) e.LeavesRemaining() }
對此文件的惟一更改是第 6行。咱們經過將所需參數傳遞給New函數來建立新員工。
如下是進行所需更改後的兩個文件的內容,
employee.go 內容
package employee import ( "fmt" ) type employee struct { firstName string lastName string totalLeaves int leavesTaken int } func New(firstName string, lastName string, totalLeave int, leavesTaken int) employee { e := employee {firstName, lastName, totalLeave, leavesTaken} return e } func (e employee) LeavesRemaining() { fmt.Printf("%s %s has %d leaves remaining", e.firstName, e.lastName, (e.totalLeaves - e.leavesTaken)) }
main.go 內容
package main import "oop/employee" func main() { e := employee.New("Sam", "Adolf", 30, 20) e.LeavesRemaining() }
運行此程序將輸出,
Sam Adolf has 10 leaves remaining
所以,您能夠理解儘管Go不支持類,但能夠有效地使用結構代替類,而且使用簽名方法 New(parameters)
替換構造函數的位置。