Golang语言基础教程:什么是方法

    技术2022-07-13  67

    1.1 什么是方法

    Go 语言中同时有函数和方法。一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集

    方法只是一个函数,它带有一个特殊的接收器类型,它是在func关键字和方法名之间编写的。接收器可以是struct类型或非struct类型。接收方可以在方法内部访问。

     

    方法能给用户自定义的类型添加新的行为。它和函数的区别在于方法有一个接收者,给一个函数添加一个接收者,那么它就变成了方法。接收者可以是值接收者,也可以是指针接收者。

    在调用方法的时候,值类型既可以调用值接收者的方法,也可以调用指针接收者的方法;指针类型既可以调用指针接收者的方法,也可以调用值接收者的方法。

    也就是说,不管方法的接收者是什么类型,该类型的值和指针都可以调用,不必严格符合接收者的类型。

     

    1.2 方法的语法

    定义方法的语法

    func (t Type) methodName(parameter list)(return list) { } func funcName(parameter list)(return 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 }

    可以定义相同的方法名

    示例代码:

    package main ​ import ( "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 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 struct作为参数传递给它。这个程序也产生了相同的输出:Salary of Sam Adolf is $5000.。

    为什么我们可以用函数来写相同的程序呢?有以下几个原因

    Go不是一种纯粹面向对象的编程语言,它不支持类。因此,类型的方法是一种实现类似于类的行为的方法。相同名称的方法可以在不同的类型上定义,而具有相同名称的函数是不允许的。假设我们有一个正方形和圆形的结构。可以在正方形和圆形上定义一个名为Area的方法。这是在下面的程序中完成的。

    1.4 变量作用域

    作用域为已声明标识符所表示的常量、类型、变量、函数或包在源代码中的作用范围。

    Go 语言中变量可以在三个地方声明:

    函数内定义的变量称为局部变量函数外定义的变量称为全局变量函数定义中的变量称为形式参数

    局部变量

    在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量。

    全局变量

    在函数体外声明的变量称之为全局变量,首字母大写全局变量可以在整个包甚至外部包(被导出后)使用。

    package main ​ import "fmt" ​ /* 声明全局变量 */ var g int ​ func main() { ​ /* 声明局部变量 */ var a, b int ​ /* 初始化参数 */ a = 10 b = 20 g = a + b ​ fmt.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 main ​ import ( "fmt" ) ​ type Rectangle struct { width, height int } ​ func (r *Rectangle) setVal() { r.height = 20 } ​ func main() { p := Rectangle{1, 2} s := p p.setVal() fmt.Println(p.height, s.height) }

    结果

    20 2

    如果没有那个*,则值就是2 2

    1.5 method继承

    method是可以继承的,如果匿名字段实现了一个method,那么包含这个匿名字段的struct也能调用该method

    package main ​ import "fmt" ​ type Human struct { name string age int phone 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 main ​ import "fmt" ​ type Human struct { name string age int phone 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 方法是可以继承和重写的存在继承关系时,按照就近原则,进行调用
    Processed: 0.012, SQL: 9