一、method
带有接收者的函数,称之为method。
假设定义了一个struct叫长方形,现在要计算它的面积,那么按照面向过程的思路会如下实现:
packeage main
import "fmt"
type Rectange struct {
width, height float64
}
func area(r Rectangle) float64 {
return r.width*r.height
}
func main() {
r1 := Rectangle(12, 2)
r2 := Rectangle(9, 4)
fmt.Println("Area of r1 is: ", area(r1))
fmt.Println("Area of r2 is: ", area(r2))
}
这段代码中,area不是Rectangle的方法,所以需要将r1,r2作为参数传入以计算面积。我们知道C语言是不能重载的,为了解决新增类型比如圆、五边形等问题则需要为此定义多个不同名的面积计算函数,比如:area_circle、area_triangle。这种方式相对于C++的重载来看不是很优雅,在Go语言中,用了类似的解决方案,即method。
method附属在一个给定的类型上,它的语法和函数的声明语法几乎一样,只是在func后面增减了一个receiver(也就是method所依从的主体),简单点说就是struct拥有方法area。method的语法如下:
func (r ReceiverType) funcName(parameters) (results)
将前面例子改造如下:
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
}
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())
}
使用method的时候需要注意以下几点:
(1)虽然method的名字一模一样,但是如果接收者不一样,那么method就不一样。
(2)method里面可以访问接收者的字段。
(3)调用method通过访问,就像struct里面访问字段一样。
在上面的例子中,method方法中的Receiver是以值传递,而非引用传递。实际上,Receiver还可以是指针,二者区别在于:指针作为Receiver会对实例对象的内容发生操作,而普通类型作为Receiver仅仅是以副本作为操作对象,并不对原实例对象发生操作。
method其实不仅仅可以用在struct上,而且可以用在任何自定义的类型、内置类型、struct等上面。当然struct也是自定义类型中的一个特例,你还可以通过如下形式来申明自定义类型:
type typeName typeLiteral
具体例子如下:
type ages int
type money float32
type months map[string]int
实际上自定义类型只是为内置类型定义了一个别名而已,类似于C语言中的typedef,如上面所示用ages替代了int。
二、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
}
//在human上定义一个method
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-YYY"}, "MIT"}
sam := Employee{Human{"Sam", 45, "111-888-XXX"}, "Golang Inc"}
mark.SayHi()
sam.SayHi()
}
三、method重写
method还能重写,类似于匿名字段冲突的处理方式。
我们可以在Emplyee上面定义一个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)
}
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()
}
看了上面的代码是否看到了C++的影子啊,呵呵。
通过这些内容,我们可以设计出基本的面向对象的程序,但是Go语言的面向对象非常简单,没有任何的私有、公有关键字,通过大小写来实现(大写开头的为公有,小写开头的为私有),方法也也同样适用这个原则。
阅读(466) | 评论(0) | 转发(0) |