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)
替换构造函数的位置。