Go Structs 中的方法
我们知道 Go 中的 Struct 可以像其它语言中的类一样,包含数据,同样的,它也可以通过定义方法来保存属性它自己的行为,将一个方法绑定、关联到一个 Struct 或者任何一个其它的类型中是非常简单的,它就像普通的函数定义一样,唯一的不同是你需要为其指定一个类型(type)。
下面这个示例是一个普通的函数定义,它将返回一个类型为 int 的值:
func myFunc() int {
// code
}
上面定义的函数将不接收任何参数,返回一个 int 类型的值,如果我们要将其绑定或者说关联到一个Struct上面的话,只需要做很少的修改即可:
type myType struct {}
func (m myType) myFunc() int {
// code
}
让我们现在来扩展一下前面一篇文章《Go 中的 Struct——无类实现面向对象编程》中的那个 Rectangle 示例, 我们为其添加一个名为 Area 的方法,用来计算长方形的面积:
package main
import "fmt"
type Rectangle struct {
width, length int
}
func (r Rectangle) Area() int {
return r.width * r.length
}
func main() {
r := Rectangle{4,3}
fmt.Println("Rectangle is:", r)
fmt.Println("Rectangle area is:", r.Area())
}
输出为:
Rectangle is: {4 3}
Rectangle area is: 12
许多面向对象的程序开发语言都有一个类似 this 或者 self 的概念,用它来代表并访问当前的实例,Go没有这样的概念,如果要将某一个函数或者方法绑定到一个Struct中,直接将一个Struct的实例作为参数传递给该方法或者函数,在上面的示例中,r 就是这个传递给方法 Area 的 Rectangle实例,它会在每一次调用 r.Area() 方法时,指向 r 自身。
同样的,你还可以传递Struct的引用,但是这与直接传递实例没有差别,因为Go会自动为你进行转换:
package main
import "fmt"
type Rectangle struct {
length, width int
}
func (r Rectangle) AreaByValue() int {
return r.length * r.width
}
func (r *Rectangle) AreaByReference() int {
return r.length * r.width
}
func main() {
r := Rectangle{4,3}
fmt.Println("Rectangle is: ", r)
fmt.Println("Rectangle area is: ", r.AreaByValue())
fmt.Println("Rectangle area is: ", r.AreaByReference())
fmt.Println("Rectangle area is: ", (&r).AreaByValue())
fmt.Println("Rectangle area is: ", (&r).AreaByReference())
}
输出为:
Rectangle is: {4 3}
Rectangle area is: 12
Rectangle area is: 12
Rectangle area is: 12
Rectangle area is: 12
在上面的代码中,我们定义了两人个很相似的方法,一个传递实例,一个传递实例的引用,我们同时又使用了实例分别调用它们,再使用了实例的地址调用它们,可以看到,四种方法的结果都是一样的。
下面我们再“绑定”一个名为 Perimeter 的方法来设置长方形的周长:
package main
import "fmt"
type Rectangle struct {
width, length int
}
func (r Rectangle) Area() int {
return r.width * r.length
}
func (r Rectangle) Perimeter() int {
return 2 * (r.length + r.width)
}
func main() {
r := Rectangle{4,3}
fmt.Println("Rectangle is:", r)
fmt.Println("Rectangle area is:", r.Area())
fmt.Println("Rectangle perimeter is:", r.Perimeter())
}
输出结果为:
Rectangle is: {4 3}
Rectangle area is: 12
Rectangle perimeter is: 14
现在你可能会想到了,既然为自定义的Struct添加方法绑定,扩展它的行为这么简单,那是否我也可以很容易扩展以有的Struct的方法呢?看下面的示例:
package main
import "fmt"
import "time"
func (t time.Time) first3Chars() string {
return t.Weekday().String()[0:3]
}
func main() {
t := time.Time{}
fmt.Println("First 3 Chars is: ", t.first3Chars())
}
输出为:
./structsmethods4.go:4: cannot define new methods on non-local type time.Time
./structsmethods4.go:9: t.first3Chars undefined (type time.Time has no field or method first3Chars)
你只能将方法或者函数绑定到相同的包内的Struct中,如果你真的需要扩展其它包,那再想想还有什么办法?其实很简单,那就是使用Go Struct中的匿名字段。
package main
import (
"fmt"
"time"
)
type myTime struct {
time.Time // 匿名字段
}
func (t myTime) first3Chars() string {
return t.Weekday().String()[0:3]
}
func main() {
m := myTime{}
fmt.Println("Full Weekday:", m.Weekday().String())
fmt.Println("First 3 chars:", m.first3Chars())
}
输出结果:
Full Weekday: Monday
First 3 chars: Mon
匿名字段中的方法
在《Go Struct中的匿名字段》这篇文章里面我讲到过了对匿名字段的方法,当 time.Time 成为了 myTime 的一个匿名字段之后,我们就能通过 myTime 访问 Time 所的方法,我们同样可以使用 myTime.String() 将自己转换成为一个字符串,让我们再来做看一看前面的示例。
在下面这个示例中,我们返回到Kitchen与House的问题上面来,我们知道了如何访问匿名字段的数据,那么也应该知道如何访问匿名字段的方法了,他们是一样的:
package main
import "fmt"
type Kitchen struct {
numOfForks int
numOfKnives int
}
func (k Kitchen) totalForksAndKnives() int {
return k.numOfForks + k.numOfKnives
}
type House struct {
Kitchen
}
func main() {
h := House{Kitchen{4,4}}
fmt.Println("Sum of forks and knives in house:", h.totalForksAndKnives())
}
输出结果为:
Sum of forks and knives in house: 8
评论已关闭