您的位置:首页 > 编程语言 > Go语言

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

2016-01-14 15:56 656 查看
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
}


让我们扩展之前写的结构体
Rectangle
Area
函数。这一次,
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.Time
myTime
的匿名字段,我们可以如同访问
time.Time
一样访问
myTime
,即我们可以调用
myTime.String()
。让我们通过更前面的一个例子更进一步说明。

下面的代码是前面
House
Kitchen
的例子。由于匿名字段,允许外围结构体直接访问内部结构体的字段,对于方法同样适用。当前,
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一种神奇的语言,让我们一起进步
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: