什么是方法
一个方法只是一个函数,它有一个特殊的接收者(receiver)类型,该接收者放在 func
关键字和函数名之间。接收者可以是结构体类型或非结构体类型。可以在方法内部访问接收者。
通过下面的语法创建一个方法:
func (t Type) methodName(parameter list) {
}
上面的代码片段创建了一个名为 methodName
的方法,该方法有一个类型为 Type
的接收者。
例子
让我们编写一个简单的程序,它创建一个结构体类型的方法并调用它。
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 }
上面程序的第 6 行,我们创建了 Employee
的一个名为 displaySalary
的方法。在 displaySalary()
方法内部可以访问它的接收者 e
(类型为 Employee
)。在第 17 行,我们使用接收者 e
,并打印它的 name
,currency
以及 salary
。
在第26行,我们使用 emp1.displaySalary()
这样的语法来调用方法。
程序的输出为:Salary of Sam Adolf is $5000
。
为什么使用方法而不是函数?
上面的程序可以使用函数而不是方法重写如下
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.name, e.currency, e.salary) } func main() { emp1 := Employee{ name: "Sam Adolf", salary: 5000, currency: "$", } displaySalary(emp1) }
在上面的程序中,我们使用 displaySalary
函数替换了方法,并将 Employee
结构体作为参数传给它。该程序的输出与上面的程序输出一样:Salary of Sam Adolf is $5000
。
为什么使用方法而不是函数?
上面的程序可以使用函数而不是方法重写如下:
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.name, e.currency, e.salary) } func main() { emp1 := Employee{ name: "Sam Adolf", salary: 5000, currency: "$", } displaySalary(emp1) }
在上面的程序中,我们使用 displaySalary
函数替换了方法,并将 Employee
结构体作为参数传给它。该程序的输出与上面的程序输出一样:Salary of Sam Adolf is $5000
。
那么为什么我们可以用函数完成同样的工作,却还要使用方法呢?这里有几个原因,我们一个一个地看。
- Go 不是一个纯面向对象的编程语言,它不支持 class 类型。因此通过在一个类型上建立方法来实现与 class 相似的行为。
- 同名方法可以定义在不同的类型上,但是 Go 不允许同名函数。假设我们有一个
Square
和Circle
两个结构体。在Square
和Circle
上定义同名的方法是合法的,比如下面的程序:
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, 5, } fmt.Printf("Area of rectangle %d ", r.Area()) c := Circle{ radius: 12, } fmt.Printf("Area of circle %f", c.Area()) }
程序的输出为:
Area of rectangle 50 Area of circle 452.389342
定义非结构体类型的方法
现在我们定义的都是结构体类型的方法。同样可以定义非结构体类型的方法,不过这里需要注意一点。为了定义某个类型的方法,接收者类型的定义与方法的定义必须在同一个包中。目前为止,我们定义的结构体和相应的方法都是在main包中的,因此没有任何问题。
package main func (a int) add(b int) { } func main() { }
在上面的程序中,第3行我们试图添加一个方法 add
给内置类型 int
。这是不允许的,因为定义方法 add
所在的包和定义类型 int
的包不是同一个包。这个程序将会报编译错误:cannot define new methods on non-local type int
。
使其工作的方法为定义内置类型的别名,然后以这个新类型为接收者创建方法。
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) }
上面的程序中,第5行,我们创建了新的类型,一个 int
的别名 myInt
。在第7行,我们定义了一个方法 add
,以 myInt
作为接收者。
程序的输出为: Sum is 15
。