Golang初级系列教程-结构体方法-Methods on structs

Golang初级系列教程-结构体方法-Methods on structs

Methods on structs

上一篇文章已经介绍结构体中可以包含属性,同样结构体中可以有方法。方法的定义和普通函数定义相似,唯一的区别是方法需要 attach to 或者说 associated with (关联)一个结构体。

比如现在想定义一个没有参数,并且返回 int 的普通函数,可能同下面代码类似

func my_func() int {
   //code
}

接下来,我们定义一个结构体叫做 my_type,这时,将如上的函数关联到 my_type,那么 my_func 就是 my_type 的一个方法。

type my_type struct { }

func (m my_type) my_func() int {
   //code
}

让我们扩展之前写的结构体 RectangleArea 函数。这一次,Area 将只为 Rectangle 服务,成为其一个方法。

package main

import "fmt"

type Rectangle struct {
    length, width int
}

func (r Rectangle) Area() int {
    return r.length * r.width
}

func main() {
    r1 := Rectangle{4, 3}
    fmt.Println("Rectangle is: ", r1)
    fmt.Println("Rectangle area is: ", r1.Area())
}
Rectangle is: {4 3}
Rectangle area is: 12

许多面向对象语言,都有一个 this 或者 self 隐式的只想当前的实例,但在 Go 中,并不存这样的概念。当定义方法是,都会给定一个变量名,如上面的例子中是 (r Rectangle),在方法中,可以使用这个变量名。

上面代码中,调用 Area 方法时,Rectangle 实例通过值传递。当然,也可以通过引用传递。对于方法而言,使用引用和值并没有太大的区别。Go 本身会自动识别,并且完成转换。

package main

import "fmt"

type Rectangle struct {
    length, width int
}

func (r Rectangle) Area_by_value() int {
    return r.length * r.width
}

func (r *Rectangle) Area_by_reference() int {
    return r.length * r.width
}

func main() {
    r1 := Rectangle{4, 3}
    fmt.Println("Rectangle is: ", r1)
    fmt.Println("Rectangle area is: ", r1.Area_by_value())
    fmt.Println("Rectangle area is: ", r1.Area_by_reference())
    fmt.Println("Rectangle area is: ", (&r1).Area_by_value())
    fmt.Println("Rectangle area is: ", (&r1).Area_by_reference())
}

Rectangle is: {4 3}
Rectangle area is: 12
Rectangle area is: 12
Rectangle area is: 12
Rectangle area is: 12

上面代码中,一个方法传值,另一个方法传递指针。通过 r1&r1 分别调用两个方法,都能正确执行,源于 Go 内部的处理机制。

让我们继续对 Rectangle 添加一个方法 Perimeter ——计算周长。

package main

import "fmt"

type Rectangle struct {
    length, width int
}

func (r Rectangle) Area() int {
    return r.length * r.width
}

func (r Rectangle) Perimeter() int {
    return 2* (r.length + r.width)
}

func main() {
    r1 := Rectangle{4, 3}
    fmt.Println("Rectangle is: ", r1)
    fmt.Println("Rectangle area is: ", r1.Area())
    fmt.Println("Rectangle perimeter is: ", r1.Perimeter())
}
Rectangle is: {4 3}
Rectangle area is: 12
Rectangle perimeter is: 14

你可能已经注意到,这种添加方法的方式非常灵活,那么是不是也可以对 int 或者 time.Time 添加方法呢?答案是否定的。由于, Go 允许和类型定义相同的包内进行方法声明。

func (t time.Time) first5Chars() string {
    return time.LocalTime().String()[0:5]
}
cannot define new methods on non-local type time.Time

但是如果你确实想实现这样的功能,那么我们可以利用前面学到的匿名字段。

package main

import "fmt"
import "time"

type myTime struct {
    time.Time //anonymous field
}

func (t myTime) first5Chars() string {
    return t.Time.String()[0:5]
}

func main() {
    m := myTime{*time.LocalTime()} //since time.LocalTime returns an address, we convert it to a value with *
    fmt.Println("Full time now:", m.String()) //calling existing String method on anonymous Time field
    fmt.Println("First 5 chars:", m.first5Chars()) //calling myTime.first5Chars
}
Full time now: Tue Nov 10 23:00:00 UTC 2009
First 5 chars: Tue N

匿名字段的函数

深入分析上面的代码,想必你已经注意到,调用匿名字段方法的方式。由于 time.TimemyTime 的匿名字段,我们可以如同访问 time.Time 一样访问 myTime,即我们可以调用 myTime.String()。让我们通过更前面的一个例子更进一步说明。

下面的代码是前面 HouseKitchen 的例子。由于匿名字段,允许外围结构体直接访问内部结构体的字段,对于方法同样适用。当前,Kitchen 有一个方法 totalForksAndKnives(),所以 House 可以直接访问 House.totalForksAndKnives()

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 //anonymous field
}

func main() {
    h := House{Kitchen{4, 4}} //the kitchen has 4 forks and 4 knives
    fmt.Println("Sum of forks and knives in house: ", h.totalForksAndKnives())  //called on House even though the method is associated with Kitchen
}
Sum of forks and knives in house: 8

Golang一种神奇的语言,让我们一起进步

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值