1.基本介绍
在某些情况下,需要声明(定义)方法,比如:Person结构体,除了有一些字段(年龄,名字,...),Person结构体还有一些行为,比如:可以说话,跑步,...通过学习,还可以做算术题,这个时候就要用到方法才能够完成.
go中的方法是作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型都可以有方法,而不仅仅是struct
2.方法的声明和调用
type A struct {
Num int
}
func (a A) test(){
fmt.Println(a.Num)
}
对上面语法的说明:
(1).func(a A) test(){} 表示: A结构体有一方法,方法名为test
(2).(a A)体现 test方法和A类型绑定的
举例说明
package main
import(
"fmt"
)
//结构体
type Person struct{
Name string
}
type Dog struct {
}
//给Person类型绑定一个方法
func (p Person) test(){
p.Name = "jack"
fmt.Println("test() name=", p.Name) //jack
}
func main() {
//定义一个结构体
var p Person
p.Name = "tom"
p.test()//调用方法test(): test() name= tom
//下面的使用都是错误的
var dog Dog
//dog.test()// dog.test undefined (type Dog has no field or method test)
//test()//undefined: test
}
对上面代码总结
(1).test方法和Person类型绑定
(2).test方法只能通过Person类型的变量来调用,而不能直接调用,也不能使用其他类型变量来调用
(3).func (p Person) test(){} p表示哪个Person变量调用,这个p就是它的副本,这个和函数传参非常相似
(4).p这个名字是由程序员指定的,不是固定的,比如修改成person也是可以的
3.方法快速入门
(1).给Person结构体添加speak方法,输出 xxxxxx牛逼
//结构体
type Person struct{
Name string
}
//给person添加一个方法,speak,输出:xxx牛逼
func (p Person) speak(){
fmt.Println(p.Name, "牛逼") //tom
}
func main() {
//定义一个结构体
var p Person
p.Name = "tom"
p.speak()
}
(2).给Person结构体添加jisuan方法,可以计算从1+...+1000的结果,说明方法体内可以和函数一样,进行各种运算
//给person添加一个方法:计算1~1000的和,并输出
func (p Person) jisuan(){
sum := 0
for i := 1; i <= 1000; i++ {
sum += i
}
fmt.Println(sum)
}
(3).给Person结构体添加方法jisuan2,该方法可以接收一个数n,计算从1+...+n的结果
//给person添加一个方法:计算1~n的和,并输出
func (p Person) jisuan2(n int){
sum := 0
for i := 1; i <= n; i++ {
sum += i
}
fmt.Println(sum)
}
p.jisuan(2)
(4).给Person结构体添加getSum方法,可以计算两个数的和,并返回结果
//给person添加一个方法:计算两个数的和,并返回
func (p Person) getSum(n1 int, n2 int) int{
return n1 + n2
}
res := p.getSum(10,20)
4.方法的调用和传参机制
说明:
方法的调用和传参机制和函数基本一样,不一样的地方是方法调用时,会将调用方法的变量,当做实参也传递给方法,举例说明
案例1:画出前面getSum方法的执行过程+说明
对上面执行流程图说明
(1).在通过一个变量去调用方法时,其调用机制和函数一样
(2).不一样的地方是:变量调用方法时,该变量本身也会作为一个参数传递到方法(如果变量是值类型,就进行值拷贝,如果变量是引用类型,就进行地址拷贝)
案例2:
编写一个程序,要求:
1.声明一个结构体Circle,字段为radius
2.声明一个方法area和circle绑定,返回面积
3.画出area执行过程+说明
package main
import(
"fmt"
)
//声明一个结构体Circle,字段为radius
//声明一个方法area和circle绑定,返回面积
type Circle struct{
radius float64
}
func (circle Circle) area() float64 {
//计算面积
return 3.14 * circle.radius * circle.radius
}
func main() {
var circle Circle
circle.radius = 10
area := circle.area()
fmt.Println(area)
}
5.方法的声明和定义剖析
语法:
func (recevier type) methodName (参数列表) (返回值列表){
方法体
return 返回值
}
说明:
(1).参数列表:表示方法输入
(2).recevier type:表示这个方法和type这个类型进行绑定,或者说该方法作用于type类型
(3).recevier type:type可以是结构体,也可以是其他自定义类型
(4).recevier:就是type类型的一个变量(实例),比如:Person结构体的一个变量(实例)
(5).返回值列表:表示返回的值,可以多个
(6).方法主体:表示为了实现某一功能的代码块
(7).return语句不是必须的
6.方法的注意事项和细节讨论
(1).结构体类型是值传递,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式
(2).如果程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方法来处理
package main
import(
"fmt"
)
//声明一个结构体Circle,字段为radius
//声明一个方法area和circle绑定,返回面积
type Circle struct{
radius float64
}
func (circle Circle) area() float64 {
//计算面积
return 3.14 * circle.radius * circle.radius
}
//为了提高效率,通常我们方法和结构体的指针类型绑定
func (circle2 *Circle) area2() float64 {
//计算面积
//因为circle2是指针,所有我们标准的访问字段的方式:(*circle2).radius
// return 3.14 * (*circle2).radius * (*circle2).radius
//(*circle2).radius等价 circle2.radius
return 3.14 * circle2.radius * circle2.radius
}
func main() {
// var circle Circle
// circle.radius = 10
// area := circle.area()
// fmt.Println(area)
// 创建一个circle变量
var circle Circle
circle.radius = 10
// area := (&circle).area2()
//编辑器底层做了优化:(&circle).area()等价于circle.area(),编译器会自动加上&circle
area := circle.area2()
fmt.Println(area)
}
(3).go中的方法作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型,都可以有方法,而不仅仅是struct,比如:int,float32等都可以有方法,
package main
import(
"fmt"
)
/**
* go中方法作用在指定的数据类型上(即:和指定的数据类型绑定),
* 因此:自定义类型,都可以有方法,不仅仅是struct, 比如"int float64等都可以有方法
*/
type integer int
func (i integer) print(){
fmt.Println("i=", i)
}
//编写一个方法,可以改变i的值
func (i *integer) change(){
*i = *i + 1
}
func main() {
var i integer = 10
i.print() //10
i.change()
fmt.Println("i=", i) //11
}
(4).方法的访问范围控制的规则,和函数一样,方法名首字母小写,只能在本包访问,方法名首字母大写,可以在本包和其他包访问
(5).如果一个类型出现了String()这个方法,那么fmt.Println()默认会调用这个变量的String()进行输出
package main
import(
"fmt"
)
type Student struct{
Name string
Age int
}
//给*Student 实现String()
func (stu *Student) String() string {
str := fmt.Sprintf("Name=%v \t Age=%v", stu.Name, stu.Age)
return str
}
func main() {
//定义一个student
stu := Student{
Name: "Jack",
Age: 11,
}
fmt.Println(stu)//{Jack 11}
//如果实现了*Student类型的String方法,就会自动调用String方法
fmt.Println(&stu)//Name=Jack Age=11
}