现在假设有这么一个场景,你定义了一个struct叫做长方形,你现在想要计算他的面积,那么按照我们一般的思路应该会用下面的方式来实现:

  package main

  import "fmt"

  type Rectangle 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的方法实现的(类似面向对象里面的方法),而是将Rectangle的对象(如r1,r2)作为参数传入函数计算面积的。

  这样实现当然没有问题咯,但是当需要增加圆形、正方形、五边形甚至其它多边形的时候,你想计算他们的面积的时候怎么办啊?那就只能增加新的函数咯,但是函数名你就必须要跟着换了,变成area_rectangle, area_circle, area_triangle...

  像下图所表示的那样, 椭圆代表函数, 而这些函数并不从属于struct(或者以面向对象的术语来说,并不属于class),他们是单独存在于struct外围,而非在概念上属于某个struct的。

  很显然,这样的实现并不优雅,并且从概念上来说"面积"是"形状"的一个属性,它是属于这个特定的形状的,就像长方形的长和宽一样。

  基于上面的原因所以就有了method的概念,method是附属在一个给定的类型上的,他的语法和函数的声明语法几乎一样,只是在func后面增加了一个receiver(也就是method所依从的主体)。

  用上面提到的形状的例子来说,method area() 是依赖于某个形状(比如说Rectangle)来发生作用的。Rectangle.area()的发出者是Rectangle, area()是属于Rectangle的方法,而非一个外围函数。

  更具体地说,Rectangle存在字段length 和 width, 同时存在方法area(), 这些字段和方法都属于Rectangle。

  用Rob Pike的话来说就是:

  "A method is a function with an implicit first argument, called a receiver."

  method的语法如下:

  func (r ReceiverType) funcName(parameters) (results)

  下面我们用最开始的例子用method来实现:

  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 area() 分别属于Rectangle和Circle, 于是他们的 Receiver 就变成了Rectangle 和 Circle, 或者说,这个area()方法 是由 Rectangle/Circle 发出的。

  值得说明的一点是,图示中method用虚线标出,意思是此处方法的Receiver是以值传递,而非引用传递,是的,Receiver还可以是指针, 两者的差别在于, 指针作为Receiver会对实例对象的内容发生操作,而普通类型作为Receiver仅仅是以副本作为操作对象,并不对原实例对象发生操作。后文对此会有详细论述。

  那是不是method只能作用在struct上面呢?当然不是咯,他可以定义在任何你自定义的类型、内置类型、struct等各种类型上面。这里你是不是有点迷糊了,什么叫自定义类型,自定义类型不就是struct嘛,不是这样的哦,struct只是自定义类型里面一种比较特殊的类型而已,还有其他自定义类型申明,可以通过如下这样的申明来实现。

  type typeName typeLiteral

  请看下面这个申明自定义类型的代码:

  type ages int

  type money float32

  type months map[string]int

  m := months {

  "January":31,

  "February":28,

  ...

  "December":31,

  }

  看到了吗?简单的很吧,这样你就可以在自己的代码里面定义有意义的类型了,实际上只是一个定义了一个别名,有点类似于c中的typedef,例如上面ages替代了int

  好了,让我们回到method

  你可以在任何的自定义类型中定义任意多的method,接下来让我们看一个复杂一点的例子:

  package main

  import "fmt"

  const(

  WHITE = iota

  BLACK

  BLUE

  RED

  YELLOW

  )

  type Color byte

  type Box struct {

  width, height, depth float64

  color Color

  }

  type BoxList []Box //a slice of boxes

  func (b Box) Volume() float64 {

  return b.width * b.height * b.depth

  }

  func (b *Box) SetColor(c Color) {

  b.color = c

  }

  func (bl BoxList) BiggestColor() Color {

  v := 0.00

  k := Color(WHITE)

  for _, b := range bl {

  if bv := b.Volume(); bv > v {

  v = bv

  k = b.color

  }

  }

  return k

  }

  func (bl BoxList) PaintItBlack() {

  for i, _ := range bl {

  bl[i].SetColor(BLACK)

  }

  }

  func (c Color) String() string {

  strings := []string {"WHITE", "BLACK", "BLUE", "RED", "YELLOW"}

  return strings[c]

  }

  func main() {

  boxes := BoxList {

  Box{4, 4, 4, RED},

  Box{10, 10, 1, YELLOW},

  Box{1, 1, 20, BLACK},

  Box{10, 10, 1, BLUE},

  Box{10, 30, 1, WHITE},

  Box{20, 20, 20, YELLOW},

  }

  fmt.Printf("We have %d boxes in our set\n", len(boxes))

  fmt.Println("The volume of the first one is", boxes[0].Volume(), "cm³")

  fmt.Println("The color of the last one is",boxes[len(boxes)-1].color.String())

  fmt.Println("The biggest one is", boxes.BiggestColor().String())

  fmt.Println("Let's paint them all black")

  boxes.PaintItBlack()

  fmt.Println("The color of the second one is", boxes[1].color.String())

  fmt.Println("Obviously, now, the biggest one is", boxes.BiggestColor().String())

  }

  上面的代码通过const定义了一些常量,然后定义了一些自定义类型:

  1、Color作为byte的别名

  2、定义了一个struct:Box,含有三个长宽高字段和一个颜色属性

  3、定义了一个slice:BoxList,含有Box

  然后以上面的自定义类型为接收者定义了一些method:

  1、Volume()定义了接收者为Box,返回Box的容量

  2、SetColor(c Color),把Box的颜色改为c

  3、BiggestColor()定在在BoxList上面,返回list里面容量最大的颜色

  4、PaintItBlack()把BoxList里面所有Box的颜色全部变成黑色

  5、String()定义在Color上面,返回Color的具体颜色(字符串格式)

  上面的代码通过文字描述出来之后是不是很简单?我们一般解决问题都是通过问题的描述,去写相应的代码实现。(编辑:雷林鹏 来源:网络)

最新文章

  1. 使用ssh正向连接、反向连接、做socks代理的方法
  2. Deep learning with Theano 官方中文教程(翻译)(四)—— 卷积神经网络(CNN)
  3. 韩服MU
  4. Login 页面
  5. 【性能诊断】三、单功能场景的性能分析(RedGate Profiler)
  6. 提取html中的src 路径
  7. CSS区块、浮动、定位、溢出、滚动条
  8. Android Studio Gradle 缓存目录设置
  9. java二维码生成
  10. 阻止a标签的默认事件及延伸
  11. Mac上安装Docker
  12. Linux命名空间
  13. vue响应式原理
  14. Centos下查看mysql的版本
  15. Python装饰器的调用过程
  16. A bean with that name has already been defined in DataSourceConfiguration$Hikari.class
  17. django MTV架构下的网站开发步骤
  18. 基于CentOS搭建私有云服务
  19. sgu 169 Numbers
  20. wpf 水波进度条 用户控件

热门文章

  1. mysql 客户端命令行下 直接查询并导出数据
  2. 利用VMware克隆linux虚拟机需要注意的事项
  3. EasySQLMAIL使用实践系列
  4. 初级dba学习之路参考
  5. 1.Oracle数据库查看用户锁表和对表解锁的sql语句
  6. vue指令详解
  7. python16_day12【html、css】
  8. pycharm修改配置
  9. git--公司中暂时用到的命令操作
  10. SqlHelper简单实现(通过Expression和反射)8.Sql Server数据处理类