二十五.golang的结构体
Go 不支持类,而是提供了[结构体]。结构体中可以添加[方法]。这样可以将数据和操作数据的方法绑定在一起,实现与类相似的效果。
为了加深理解,我们来编写一个示例吧。
在示例中,我们创建一个自定义[包],它帮助我们更好地理解,结构体是如何有效地取代类的。
在你的 Go 工作区创建一个名为 oop 的文件夹。在 opp 中再创建子文件夹 employee。在 employee 内,创建一个名为 employee.go 的文件。
workspacepath -> oop -> employee -> employee.go
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)) }
在上述程序里,第 1 行指定了该文件属于 employee 包。而第 7 行声明了一个 Employee 结构体。在第 14 行,结构体 Employee 添加了一个名为 LeavesRemaining 的方法。该方法会计算和显示员工的剩余休假数。于是现在我们有了一个结构体,并绑定了结构体的方法,这与类很相似。
接着在 oop
现在目录结构如下所示:
workspacepath -> oop -> employee -> employee.go
workspacepath -> oop -> 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()(第 12 行),我们调用了 Employee 的
由于有自定义包,这个程序不能在 go playground 上运行。你可以在你的本地运行,在 workspacepath/bin/oop 下输入命令 go install opp,程序会打印输出:
Sam Adolf has 10 leaves remaining
package main import "oop/employee" func main() { var e employee.Employee e.LeavesRemaining() }
has 0 leaves remaining
你可以看到,使用 Employee 创建的零值变量没有什么用。它没有合法的姓名,也没有合理的休假细节。
在像 Java 这样的 OOP 语言中,是使用构造器来解决这种问题的。一个合法的对象必须使用参数化的构造器来创建。
让我修改一下原先的代码,使得每当创建 employee 的时候,它都是可用的。
首先应该让 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 结构体的首字母改为小写 e,也就是将 type Employee struct 改为了 type employee struct。通过这种方法,我们把 employee
同样,我们还修改了 LeavesRemaining() 的方法。
现在由于 employee 不可引用,因此不能在其他包内直接创建 Employee 类型的变量。于是我们在第 14 行提供了一个可引用的 New 函数,该函数接收必要的参数,返回一个新创建的 employee 结构体变量。
这个程序还需要一些必要的修改,但现在先运行这个程序,理解一下当前的修改。如果运行当前程序,编译器会报错,如下所示:
go/src/constructor/main.go:6: undefined: employee.Employee
这是因为我们将 Employee 设置为不可引用,因此编译器会报错,提示该类型没有在 main.go 中定义。很完美,正如我们期望的一样,其他包现在不能轻易创建零值的 employee 变量了。我们成功地避免了创建不可用的 employee 结构体变量。现在创建 employee 变量的唯一方法就是使用 New 函数。
package main import "oop/employee" func main() { e := employee.New("Sam", "Adolf", 30, 20) e.LeavesRemaining() }
该文件唯一的修改就是第 6 行。通过向 New 函数传入所需变量,我们创建了一个新的 employee 结构体变量。
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