12-方法
方法
什么是方法
方法其实就是一个函数,在 func
这个关键字和方法名中间加入了一个特殊的接收器类型。接收器可以是结构体类型或者是非结构体类型。接收器是可以在方法的内部访问的。(我们也可以这么理解,结构体是一系列属性的集合,方法和结构体写一块类似于python中的类。)
语法
func (t Type) methodName(parameter list) {
}
Type是结构体(接收器),methodName是方法名。
方法示例 (下面两个示例,一个是对方法没有传参的,一个是传参之后修改nam值的)
package main import "fmt" //结构体 type Person struct { name string age int sex int } //创建一个方法:给Person结构体一个打印名字的方法 func (a Person)printName() { fmt.Println(a.name) //bb } //创建一个方法:传入一个参数name,修改name值 func (a Person)changeName(name string) { a.name=name fmt.Println(a) //{jj 0 0} } func main() { p:=Person{name:"bb"} //给结构体赋值 p.printName() //调用方法printName获取name值 p1:=Person{name:"xxx"} p1.changeName("jj") //传参,调用方法changeName修改name值 fmt.Println(p1) //{xxx 0 0} }
方法中结构体是指针的情况:
package main import "fmt" //结构体 type Person struct { name string age int sex int } //创建一个方法:结构体是指针情况下,修改name值 func (a *Person)changeName1(name string) { a.name=name fmt.Println(*a) } func main() { var p2 =&Person{name:"zzz"} //试验不加&也行 p2.changeName1("kkk") } #结果 {kkk 0 0}
为什么已经有函数了还需要方法呢?
package main import ( "fmt" ) type Employee struct { name string salary int currency string } /* displaySalary()方法被转化为一个函数,把 Employee 当做参数传入。 */ 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 不是纯粹的面向对象编程语言
,而且Go不支持类。因此,基于类型的方法是一种实现和类相似行为的途径。- 相同的名字的方法可以定义在不同的类型上,而相同名字的函数是不被允许的(******)。假设我们有一个
Square
和Circle
结构体。可以在Square
和Circle
上分别定义一个Area
方法。见下面的程序。
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 %d\n", r.Area()) c := Circle{ radius: 12, } fmt.Printf("Area of circle %f", c.Area()) }
什么时候使用指针接收器,什么时候使用值接收器
package main import "fmt" //结构体 type Person struct { name string age int sex int } //创建一个方法:值接收器 func (a Person)changeName(name string) { a.name=name } //创建一个方法:指针接收器 func (a *Person)changeAge(age int) { a.age=age } func main() { p:=Person{name:"xxx",age:20} fmt.Println(p.name) //xxx p.changeName("jj") fmt.Println(p.name) //xxx fmt.Println(p.age) //20 p.changeAge(10) fmt.Println(p.age) //10 }
指针接收器与值接收器
func (值接收器)changeName(name string):在内部修改值,不会影响外部的值
func (指针接收器)changeAge(age int):在内部修改值,会影响外部的值
那什么时候使用指针接收器,什么什么时候使用值接收器:当你想修改原值的时候就用指针接收器
匿名字段的方法
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() //访问 address 结构体的 fullAddress 方法 }
在上面程序的第 32 行,我们通过使用 p.fullAddress()
来访问 address
结构体的 fullAddress()
方法。明确的调用 p.address.fullAddress()
是没有必要的。该程序输出:
Full address: Los Angeles, California
在方法中使用值接收器和在函数中使用值参数
在方法中使用指针接收器和在函数中使用指针参数
package main import "fmt" type Person2 struct { name string age int sex int } //方法中使用值接收器 func (a Person2)printName() { fmt.Println(a.name) } //方法中使用指针接收器 func (a *Person2)printName2() { fmt.Println(a.name) } //函数中使用值参数 func printName(a Person2) { fmt.Println(a.name) } //在函数中使用指针参数 func printName2(a *Person2) { fmt.Println(a.name) } func main() { p:=&Person2{name:"lqz"} //调用值接收器 p.printName() //调用指针接收器 p.printName2() //调用值函数 printName(p) //调用指针函数 printName2(&p) //不管是指针接收器还是值接收器,都可以使用值来调用 //不管是指针接收器还是值接收器,都可以使用指针来调用 //函数中,是什么参数,就得传什么参数 }
在非结构体上的方法
package main import "fmt" //重命名 type MyInt int func (i *MyInt)add() { (*i)++ fmt.Println(*i) } func main() { var a MyInt=10 a.add() fmt.Println(a) }