为什么go函数的定义不同?

时间:2014-03-11 00:15:30

标签: go

我正在努力学习Go。当我以为我理解一个函数是什么,如何使用它并且想要进入接口时我就陷入了这个问题(来源Go blog

package main

import "fmt"

//define a Rectangle struct that has a length and a width
type Rectangle struct { 
   length, width int
}

//write a function Area that can apply to a Rectangle type
func (r Rectangle) Area() int {
   return r.length * r.width
}

func main() {
   r := Rectangle{length:5, width:3} //define a new Rectangle instance with values for its properties
   fmt.Println("Rectangle details are: ",r)  
   fmt.Println("Rectangle's area is: ", r.Area())
}

为什么我们有func (r Rectangle) Area() int而不是func Area(r Rectangle) int?有什么不同吗?

3 个答案:

答案 0 :(得分:9)

这意味着您的函数已附加到某个类型。您可以为任何用户定义的类型执行此操作。区别在于您如何调用该函数,而不是使用:

a := Area(r);

你将使用:

a := r.Area();

这也意味着如果您定义类似

的方法
func (r Rectangle) String() string

Rectangle会自动实施Stringer,您将获得打印Rectangle的功能。

答案 1 :(得分:4)

它们在执行时功能相同,但在语义上有所不同。表单func F(obj T)(在本例中为func Area(r Rectangle))的函数与编写代码func (obj T) F()(在本例中为func (r Rectangle) Area())相同 - 只要方法身体也是一样的。

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

//...
r.Area()

等于

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

他们在返回相同的东西的意义上是平等的。实际上,如果用反射检查函数,你会发现方法接收器实际上被认为是内部的第一个参数。什么等于接口实现。如果你有一个界面

type Areaer interface {
  Area() int
}

(只是使用Areaer的东西,它很奇怪,但推荐的命名约定)。前一个实现将导致Rectangle实现Area,但后者不会。含义

func PrintArea(a Areaer) {
    fmt.Println(a.Area())
}

调用时PrintArea(r)将在第一个实现时起作用,但不适用于第二个。

何时使用免费功能vs方法很大程度上是一种设计选择。通常,一个方法将 ON 连接的接收器,而一个函数将 USE 连接的接收器。一个例子是Graph可以知道它的后继者或节点,但像A *这样的函数可以是一个接收图形的函数。

对于矩形,WidthHeightAreaGrowSetPosition之类的东西可能是好方法,因为它们是矩形的东西知道自己(或者可以做自己),而像Intersects那样确定两个矩形是否相交的函数可能是一个以两个矩形作为参数的函数。

当然,你会发现很多例外,无论是由于糟糕的设计还是仔细考虑。但是,我认为这是对一般事态的理解。

答案 2 :(得分:2)

这是一种与功能相对的方法。通常,您选择并选择您喜欢使用的方法(尽管当您有多个函数都在同一类型的对象上时,方法通常更有意义)。唯一需要注意的是,您只能使用带接口的方法。例如:

type Shape interface {
    Area() int
}

type Rectangle struct {
    ...
}

func (r *Rectangle) Area() int {
    ...
}

在这种情况下,为了满足Shape接口,Area()必须是一种方法。具有:

func Area(r *Rectangle) int {
    ...
}

不满足界面。