一、方法
1.1 什么是方法
Go 语言中同时有函数和方法。一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集
方法只是一个函数,它带有一个特殊的接收器类型,它是在func关键字和方法名之间编写的。接收器可以是struct类型或非struct类型。接收方可以在方法内部访问。
方法能给用户自定义的类型添加新的行为。它和函数的区别在于方法有一个接收者,给一个函数添加一个接收者,那么它就变成了方法。接收者可以是值接收者,也可以是指针接收者。
在调用方法的时候,值类型既可以调用值接收者的方法,也可以调用指针接收者的方法;指针类型既可以调用指针接收者的方法,也可以调用值接收者的方法。
也就是说,不管方法的接收者是什么类型,该类型的值和指针都可以调用,不必严格符合接收者的类型。
1.2 方法的语法
定义方法的语法
func (t Type) methodName(parameter list)(return list){
}
func funcName(parameter list)(return list){
}
示例代码:
package mainimport ("fmt"
)type Employee struct {name stringsalary intcurrency 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
}
可以用定义相同的方法名
示例代码:
package mainimport ("fmt""math"
)type Rectangle struct {width, height float64
}
type Circle struct {radius float64
}func (r Rectangle) area() float64 {return r.width * r.height
}
//该 method 属于 Circle 类型对象中的方法
func (c Circle) area() float64 {return c.radius * c.radius * math.Pi
}
func main() {r1 := Rectangle{12, 2}r2 := Rectangle{9, 4}c1 := Circle{10}c2 := Circle{25}fmt.Println("Area of r1 is: ", r1.area())fmt.Println("Area of r2 is: ", r2.area())fmt.Println("Area of c1 is: ", c1.area())fmt.Println("Area of c2 is: ", c2.area())
}
运行结果:
Area of r1 is: 24
Area of r2 is: 36
Area of c1 is: 314.1592653589793
Area of c2 is: 1963.4954084936207
- 虽然method的名字一模一样,但是如果接收者不一样,那么method就不一样
- method里面可以访问接收者的字段
- 调用method通过.访问,就像struct里面访问字段一样
1.3 方法和函数
既然我们已经有了函数,为什么还要使用方法?
示例代码:
package mainimport ( "fmt"
)type Employee struct { name stringsalary intcurrency 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 struct作为参数传递给它。这个程序也产生了相同的输出:Salary of Sam Adolf is $5000.。
为什么我们可以用函数来写相同的程序呢?有以下几个原因
Go不是一种纯粹面向对象的编程语言,它不支持类。因此,类型的方法是一种实现类似于类的行为的方法。
相同名称的方法可以在不同的类型上定义,而具有相同名称的函数是不允许的。假设我们有一个正方形和圆形的结构。可以在正方形和圆形上定义一个名为Area的方法。这是在下面的程序中完成的。
1.4 变量作用域
作用域为已声明标识符所表示的常量、类型、变量、函数或包在源代码中的作用范围。
Go 语言中变量可以在三个地方声明:
函数内定义的变量称为局部变量
函数外定义的变量称为全局变量
函数定义中的变量称为形式参数
局部变量
在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量。
全局变量
在函数体外声明的变量称之为全局变量,首字母大写全局变量可以在整个包甚至外部包(被导出后)使用。
package mainimport "fmt"/* 声明全局变量 */
var g intfunc main() {/* 声明局部变量 */var a, b int/* 初始化参数 */a = 10b = 20g = a + bfmt.Printf("结果: a = %d, b = %d and g = %d\n", a, b, g)
}
运算结果:
结果: a = 10, b = 20 and g = 30
形式参数
形式参数会作为函数的局部变量来使用
指针作为接收者
若不是以指针作为接收者,实际只是获取了一个copy,而不能真正改变接收者的中的数据
func (b *Box) SetColor(c Color) {b.color = c
}
示例代码:
package mainimport ("fmt"
)type Rectangle struct {width, height int
}func (r *Rectangle) setVal() {r.height = 20
}func main() {p := Rectangle{1, 2}s := pp.setVal()fmt.Println(p.height, s.height)
}
运算结果:
20 2
如果没有那个*,则值就是2 2
1.5 method继承
method是可以继承的,如果匿名字段实现了一个method,那么包含这个匿名字段的struct也能调用该method
package mainimport "fmt"type Human struct {name stringage intphone string
}
type Student struct {Human //匿名字段school string
}
type Employee struct {Human //匿名字段company string
}func (h *Human) SayHi() {fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func main() {mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}mark.SayHi()sam.SayHi()
}
运算结果:
Hi, I am Mark you can call me on 222-222-YYYY
Hi, I am Sam you can call me on 111-888-XXXX
1.6 method重写
package mainimport "fmt"type Human struct {name stringage intphone string
}
type Student struct {Human //匿名字段school string
}
type Employee struct {Human //匿名字段company string
}//Human定义method
func (h *Human) SayHi() {fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}//Employee的method重写Human的method
func (e *Employee) SayHi() {fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,e.company, e.phone) //Yes you can split into 2 lines here.
}
func main() {mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}mark.SayHi()sam.SayHi()
}
运算结果:
Hi, I am Mark you can call me on 222-222-YYYY
Hi, I am Sam, I work at Golang Inc. Call me on 111-888-XXXX
方法是可以继承和重写的
存在继承关系时,按照就近原则,进行调用