Golang学习之方法

方法

  • Go中虽没有class,但依旧有method
  • 通过显示说明receiver来实现与某个类型的结合
  • 只能为同一个包中的类型定义方法
  • receiver可以是类型的值或者指针
  • 不存在方法重载
  • 可以使用值或指针来调用方法,编译器会自动完成转换
  • 从某种意义上来说,方法是函数的语法糖,因为receiver其实就是方法所接收的第一个参数(Method Value vs. Method Expression)
  • 如果外部结构和嵌入结构存在同名方法,则优先调用外部结构的方法
  • 类型别名不会拥有底层类型所附带的方法
  • 方法可以调用结构中的非公开字段

Go 语言中同时有函数和方法。一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集。语法格式如下:

func (variable_name variable_data_type) function_name() [return_type]{
   /* 函数体*/
}

方法是特殊的函数,定义在某一特定的类型上,通过类型的实例来进行调用,这个实例被叫接收者(receiver)。

注意: Go语言不允许为简单的内置类型添加方法,所以下面定义的方法是非法的。

package main

import(
  "fmt"
)


func Add(a ,b int){         //函数合法
  fmt.Println(a+b)
}

func (a int) Add (b int){    //方法非法!不能是内置数据类型
  fmt.Println(a+b)
}

合法的方法定义如下:

package main

import(
  "fmt"
)

type myInt int

func Add(a ,b int){             //函数
  fmt.Println(a+b)
}

func (a myInt) Add (b myInt){   //方法
  fmt.Println(a+b)
}

func main() {
        a, b := 3,4
        var aa,bb myInt = 3,4
        Add(a,b)
        aa.Add(bb)
  }

上面的表达式aa.Add称作选择子(selector),它为接收者aa选择合适的Add方法。

receiver也是参数,涉及到指针传递还是值的传递
当调用一个函数时,会对其每一个参数值进行拷贝,如果一个函数需要更新一个变量,或者函数的其中一个参数实在太大我们希望能够避免进行这种默认的拷贝,这种情况下我们就需要用到指针了。对应到我们这里用来更新接收器的对象的方法,当这个接受者变量本身比较大时,我们就可以用其指针而不是对象来声明方法,,如下:

func (p *Point) ScaleBy(factor float64) {
    p.X *= factor
    p.Y *= factor
}

想要调用指针类型方法(*Point).ScaleBy,只要提供一个Point类型的指针即可,像下面这样。

r := &Point{1, 2}
r.ScaleBy(2)
fmt.Println(*r) // "{2, 4}"

或者这样:

p := Point{1, 2}
pptr := &p
pptr.ScaleBy(2)
fmt.Println(p) // "{2, 4}"

或者这样:

p := Point{1, 2}
(&p).ScaleBy(2)
fmt.Println(p) // "{2, 4}"

不过后面两种方法有些笨拙。幸运的是,go语言本身在这种地方会帮到我们。如果接收器p是一个Point类型的变量,并且其方法需要一个Point指针作为接收器,我们可以用下面这种简短的写法:

p.ScaleBy(2)

编译器会隐式地帮我们用&p去调用ScaleBy这个方法。这种简写方法只适用于“变量”,包括struct里的字段比如p.X,以及array和slice内的元素比如perim[0]。

package main

import (
    "fmt"
)

type A struct {
    Name string
}

type B struct {
    Name string
}

func main() {
    a := A{}
    a.Print()
    fmt.Println(a.Name)
    b := B{}
    b.Print()
    fmt.Println(b.Name)
}

//编译器根据接收者的类型,来判断它是属于哪个方法
func (a *A) Print() { //加上*代表指针传递
    //取一个变量a,a就是接收者,它的接收者的类型就是structA,Print就是方法的名称,参数在Print()的括号中定义
    //receiver就是这个函数的第一个接收者,而且是强制规定的,这个时候就变成了一个方法
    a.Name = "AA"
    fmt.Println("A")
}
func (b B) Print() { //这里的b并不是以指针传递
    b.Name = "BB"
    fmt.Println("B")
}

运行结果是:

A
AA
B

结果所示:值类型不使用指针,在这个方法结束之后,值不会被修改

方法值和方法表达式

package main

import (
    "fmt"
)

type TZ int

func main() {
    var a TZ
    a.Print()       //Method Value
    (*TZ).Print(&a) //Method Expression  这是两种不同的调用方法

}
func (a *TZ) Print() {
    fmt.Println("TZ")
}

方法访问权限

package main

import (
    "fmt"
)

type A struct {
    mm string //首字母小写代表私有字段
    //首字母的大小写还是以包为级别来界定它的公有还是私有
}

func main() {
    a := A{}
    a.Print()
    fmt.Println(a.mm)

}
func (a *A) Print() {
    a.mm = "123"
    fmt.Println(a.mm)
}

运行结果是:

123
123

打印结果证明方法当中可以访问结构当中的私有字段,类似与其他面向对象编程语言当中class中的方法可以访问其中的私有字段

封装
Go语言只有一种控制可见性的手段:大写首字母的标识符会从定义它们的包中被导出,小写字母的则不会。

公私有
方法、结构、变量、常量等,针对包的范围。

  • 首字母大写代表 public
  • 首字母小写代表 pirvate

  • 每个目录只有一个包
  • main包包含可执行入口
  • 为结构定义的方法,必须放在一个包下,可以是不同的文件

猜你喜欢

转载自blog.csdn.net/huang_yong_peng/article/details/82951865