Golang教程:(十七)方法
原文:https://golangbot.com/methods/ 欢迎来到Golang系列教程的第十七篇。 什么是方法一个方法只是一个函数,它有一个特殊的接收者(receiver)类型,该接收者放在 通过下面的语法创建一个方法: func (t Type) methodName(parameter list) {
}
上面的代码片段创建了一个名为 例子让我们编写一个简单的程序,它创建一个结构体类型的方法并调用它。 package main
import (
"fmt"
)
type Employee struct {
name string
salary int
currency string
}
/* displaySalary() method has Employee as the receiver type */
func (e Employee) displaySalary() {
fmt.Printf("Salary of %s is %s%d",e.name,e.currency,e.salary)
}
func main() {
emp1 := Employee {
name: "Sam Adolf",salary: 5000,currency: "$",}
emp1.displaySalary() //Calling displaySalary() method of Employee type
}
在 Playground 中运行 上面程序的第 6 行,我们创建了 在第26行,我们使用 程序的输出为: 为什么使用方法而不是函数?上面的程序可以使用函数而不是方法重写如下: package main
import (
"fmt"
)
type Employee struct {
name string
salary int
currency string
}
/* displaySalary() method converted to function with Employee as parameter */
func displaySalary(e Employee) {
fmt.Printf("Salary of %s is %s%d",e.salary)
}
func main() {
emp1 := Employee{
name: "Sam Adolf",}
displaySalary(emp1)
}
在 Playground 中运行 在上面的程序中,我们使用 那么为什么我们可以用函数完成同样的工作,却还要使用方法呢?这里有几个原因,我们一个一个地看。
package main
import (
"fmt"
"math"
)
type Rectangle struct {
length int
width int
}
type Circle struct {
radius float64
}
func (r Rectangle) Area() int {
return r.length * r.width
}
func (c Circle) Area() float64 {
return math.Pi * c.radius * c.radius
}
func main() {
r := Rectangle{
length: 10,width: 5,}
fmt.Printf("Area of rectangle %dn",r.Area())
c := Circle{
radius: 12,}
fmt.Printf("Area of circle %f",c.Area())
}
在 Playground 中运行 程序的输出为: Area of rectangle 50
Area of circle 452.389342
接口正是应用了这一点(译者注:相同的方法名可以用在不同的接收者类型上)。我们将在下面的教程中讨论接口的细节。 指针接收者 vs. 值接收者目前为止我们看到的都是以值作为接收者。以指针为接收者也是可以的。两者的区别在于,以指针作为接收者时,方法内部对其的修改对于调用者是可见的,但是以值作为接收者却不是。让我们通过下面的程序帮助理解。 package main
import (
"fmt"
)
type Employee struct {
name string
age int
}
/* Method with value receiver */
func (e Employee) changeName(newName string) {
e.name = newName
}
/* Method with pointer receiver */
func (e *Employee) changeAge(newAge int) {
e.age = newAge
}
func main() {
e := Employee{
name: "Mark Andrew",age: 50,}
fmt.Printf("Employee name before change: %s",e.name)
e.changeName("Michael Andrew")
fmt.Printf("nEmployee name after change: %s",e.name)
fmt.Printf("nnEmployee age before change: %d",e.age)
(&e).changeAge(51)
fmt.Printf("nEmployee age after change: %d",e.age)
}
在 Playground 中运行 上面的程序中, Employee name before change: Mark Andrew
Employee name after change: Mark Andrew
Employee age before change: 50
Employee age after change: 51
在上面的程序第36行,我们用 下面的程序使用 package main
import (
"fmt"
)
type Employee struct {
name string
age int
}
/* Method with value receiver */
func (e Employee) changeName(newName string) {
e.name = newName
}
/* Method with pointer receiver */
func (e *Employee) changeAge(newAge int) {
e.age = newAge
}
func main() {
e := Employee{
name: "Mark Andrew",e.age)
e.changeAge(51)
fmt.Printf("nEmployee age after change: %d",e.age)
}
在 Playground 运行 何时使用指针接收者,何时使用值接收者?一般来讲,指针接收者可用于对接收者的修改应该对调用者可以见的场合。 指针接收者也可用于拷贝结构体代价较大的场合。考虑一个包含较多字段的结构体,若使用值作为接收者则必须拷贝整个结构体,这样的代价很大。这种情况下使用指针接收者将避免结构体的拷贝,而仅仅是指向结构体指针的拷贝。 其他情况下可以使用值接收者。 匿名字段函数匿名字段的方法可以被包含该匿名字段的结构体的变量调用,就好像该匿名字段的方法属于包含该字段的结构体一样。 package main
import (
"fmt"
)
type address struct {
city string
state string
}
func (a address) fullAddress() {
fmt.Printf("Full address: %s,%s",a.city,a.state)
}
type person struct {
firstName string
lastName string
address
}
func main() {
p := person{
firstName: "Elon",lastName: "Musk",address: address {
city: "Los Angeles",state: "California",},}
p.fullAddress() //accessing fullAddress method of address struct
}
在 Playground 中运行 在上面的程序中,第32行,我们通过 Full address: Los Angeles,California
方法的值接收者 vs. 函数的值参数这是很多新手遇到的问题。我们将尽可能把它说明白。 当一个函数有一个值参数时,它只接受一个值参数。 当一个方法有一个值接收者时,它可以接受值和指针接收者。 让我们通过程序说明这一点。 package main
import (
"fmt"
)
type rectangle struct {
length int
width int
}
func area(r rectangle) {
fmt.Printf("Area Function result: %dn",(r.length * r.width))
}
func (r rectangle) area() {
fmt.Printf("Area Method result: %dn",(r.length * r.width))
}
func main() {
r := rectangle{
length: 10,}
area(r)
r.area()
p := &r
/* compilation error,cannot use p (type *rectangle) as type rectangle in argument to area */
//area(p)
p.area()//calling value receiver with a pointer
}
在 Playground 中运行 第12行,函数 在第25行,我们传递了一个值来调用 在第28行,我们创建了一个指向 现在来到了微妙的地方,第35行 程序的输出为: Area Function result: 50
Area Method result: 50
Area Method result: 50
方法的指针接收者 vs. 函数的指针参数与值参数相似,一个接受指针参数的函数只能接受指针,而一个接收者为指针的方法可以接受值接收者和指针接收者。 package main
import (
"fmt"
)
type rectangle struct {
length int
width int
}
func perimeter(r *rectangle) {
fmt.Println("perimeter function output:", 2*(r.length+r.width))
}
func (r *rectangle) perimeter() {
fmt.Println("perimeter method output:", 2*(r.length+r.width))
}
func main() {
r := rectangle{
length: 10,}
p := &r //pointer to r
perimeter(p)
p.perimeter()
/* cannot use r (type rectangle) as type *rectangle in argument to perimeter */
//perimeter(r)
r.perimeter()//calling pointer receiver with a value
}
在 Playground 中运行 在上面的程序中,第12行定义了一个函数 27行我们通过指针参数调用 在被注释掉的第33行,我们试图通以一个值 在第35行我们通过一个值接收者 perimeter function output: 30
perimeter method output: 30
perimeter method output: 30
定义非结构体类型的方法现在我们定义的都是结构体类型的方法。同样可以定义非结构体类型的方法,不过这里需要注意一点。为了定义某个类型的方法,接收者类型的定义与方法的定义必须在同一个包中。目前为止,我们定义的结构体和相应的方法都是在main包中的,因此没有任何问题。 package main
func (a int) add(b int) {
}
func main() {
}
在 Playground 中运行 在上面的程序中,第3行我们试图添加一个方法 使其工作的方法为定义内置类型的别名,然后以这个新类型为接收者创建方法。 package main
import "fmt"
type myInt int
func (a myInt) add(b myInt) myInt {
return a + b
}
func main() {
num1 := myInt(5)
num2 := myInt(10)
sum := num1.add(num2)
fmt.Println("Sum is",sum)
}
在 PLayground 中运行 上面的程序中,第5行,我们创建了新的类型,一个 程序的输出为: 我已经创建了一个程序,其中包含了目前为止所讨论的所有概念,可以在 github 上找到它。 对于方法的讨论就到这里。感谢阅读! 目录 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |