Go语言的method和interface(上)

写在前面

  • 从这章开始,我们应该抛弃一些中文翻译的习惯,例如function,中文有人翻译为函数,方法等。但现在Go的官方教程提供了有别于其它语言的特性——method
  • 对于Go的method和interface不作翻译,统一用英文(function和method?翻译为中文后也不好区分,还不如直接用英文)
  • 不同于C++,Go没有类,但它提供了method和interface
  • 刚看完一部分新手教程,个人认为method和interface的设定,是一种灵活性较高的做法(和C++的类相比,耦合度的区别)
  • 推荐阅读:https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/02.5.md

Method

  • 关于Method,它其实和function差不多
  • 如果学过c++或者java,应该接触过类中的成员函数,还有那些需要注意的访问限制(private,protected,public)
  • Method有点像类成员函数,但又有区别
  • 官方原话: you can define methods on types
  • 个人认为method是围绕类型而定义的
  • 且看例子:
package main

import "fmt"

type Cube struct {
	X,Y,Z float64
}

//以下是method
func (c Cube) volume() float64 {
	return c.X*c.Y*c.Z
}

func main() {
	c := Cube{2, 3, 4}
	fmt.Println(c.volume())
}
  • 上述例子中,先定义了一个结构体类型Cube,然后定义的method:volume就是围绕Cube类型展开的
    • 和一般的function差不多,func关键字;function的定义是先名字的,而method是先“参数“,这里的”参数“,就是receiver参数
    • method是围绕receiver参数来展开的,这就有点像是receiver的“成员函数”了
    • 定义好了receiver参数后才是method的名字,返回值类型,然后是具体的操作
    • method的使用方法:定义一个receiver类型的变量,由该变量调用相关的method
    • 官方原话:a method is just a function with a receiver argument.
    • 我个人倾向于认为它是receiver类型的成员函数(目前是这样,因为了解不够多)
  • 关于receiver的类型,是有限制的
    • 说到这个问题,有必要再重申一下,在官方教程的开始,有这么一句话:Every Go program is made up of packages.
    • 而receiver的类型,不能是其他packages中定义好的,例如你不能用int作为receiver的类型,因为这是内置的类型
    package main
    
    import "fmt"
    
    type MyInt int
    
    func (i MyInt) increase() int {
        return int(i)     //这里一定要进行类型转换,和函数返回类型一致,就算是别名,在编译器里也认为不同
    }
    
    func main() {
        i := MyInt(2)
        fmt.Println(i.increase())
    }
    
  • receiver还可以是指针类型
    • 但同样也不能是其他packages定义号的,例如*int作为receiver的类型是不允许的
    • 这部分,建议不懂的读者可以看看C++引用和指针的内容
    package main
    
    import "fmt"
    
    type Cube struct {
        X, Y, Z float64
    }
    
    func (c Cube) volume() float64 {
        return c.X*c.Y*c.Z     //这里不用转换,因为Cube里的X,Y,Z都是float64类型
    }
    
    //以下就是指针类型的receiver(pointer receiver),相当于引用,会改变调用它的receiver的内容
    //如果不用指针类型(也就是用value receiver),则改变的值只是receiver的copy,对调用它的receiver没有任何改变
    func (c *Cube) Scale(f float64) {
        c.X = c.X * f
        c.Y = c.Y * f
        c.Z = c.Z * f
    }
    
    func main() {
        c := Cube{2, 3, 4}      //value receiver
        p := &Cube{2, 3, 4}    //pointer receiver
    
        //使用value receiver变量来调用pointer receiver变量的Scale方法
        //这时,编译器会自动处理成(&c).Scale(n)
        //n指的是参数,在这里的例子为2
        c.Scale(2)
        p.Scale(2)
    
        //使用pointer receiver变量来调用value receiver变量的volume方法
        //这时,编译器会处理成(*p).volume()
        fmt.Println(c.volume())
        fmt.Println(p.volume())
    }
    
    • 请读者仔细观察上述代码和注释,能发现method的使用,简化了value receiver和pointer reciver
    • 但是,对于一般的function,是不作这种类型的简化的
    • 那么用value receiver还是pointer receiver呢?
    • 官方推荐用pointer receiver,原因有二
      • 可以改变receiver的值
      • 对于较大的数据结构,引用效率高

interface

  • interface有点像c++的抽象类,而interface里的成员就像c++的纯虚函数
  • 而实现interface的成员,是通过method中的receiver
  • interface并不能像method一样,自动识别pointer receiver或value receiver
package main

import (
        "fmt"
        "math"
)

type OneAdder interface {
        OneAdd() int
}

type MyInt int

func (i MyInt) OneAdd() int {
        return int(i+1)    //类型转换
}

type Suber interface {
        Sub() float64
}

type MyFloat float64

func (f *MyFloat) Sub()  float64 {
        return float64((*f) - 1)    //类型转换
}

func main() {
        var a OneAdder
        i := MyInt(1)
        a = i
        fmt.Println(a.OneAdd())

        var b Suber
        /*
        这种写法会报错
        j := &MyFloat(math.Pi)
        b = j
        */
        j := MyFloat(math.Pi)
        b = &j
        fmt.Println(b.Sub())
}
  • 还有比较简便的写法,隐式实现一个接口
package main

import (
        "fmt"
        "math"
)

type Adder interface {
        Add() float64
}

type MyFloat float64

func (f MyFloat) Add() float64 {
        return float64(f+1)
}

type Volumer interface {
        Volume() float64
}

type Cube struct {
        X,Y,Z float64
}

func (c Cube) Volume() float64 {
        return c.X*c.Y*c.Z
}

func main() {
        var a Adder = MyFloat(math.Pi)
        fmt.Println(a.Add())

        var v Volumer = Cube{math.Pi, 1, 1}
        fmt.Println(v.Volume())
}

  • 当然,pointer receiver也是允许隐式实现接口的
  • 但是,有些情况下不行,注意示例
package main

import (
        "fmt"
        "math"
)

type Adder interface {
        Add() float64
}

type MyFloat float64

//这里改成了pointer receiver
func (f *MyFloat) Add() float64 {
        return float64((*f)+1)
}

type Volumer interface {
        Volume() float64
}

type Cube struct {
        X,Y,Z float64
}

func (c *Cube) Volume() float64 {
        return c.X*c.Y*c.Z
}

func main() {
        // 特殊情况,要设定中间变量
        var a Adder
        p := MyFloat(math.Pi)   //注意到,这里是类型转换
        a = &p
        fmt.Println(a.Add())

        // 一般情况
        var v Volumer = &Cube{math.Pi, 1, 1}    //注意到,这里是初始化
        fmt.Println(v.Volume())
}
  • interface value相当于一个元组(value, type)
  • 当receiver没有初始化为某个值时,将为nil
  • 当interface value没有初始化为某个值时,元组会变为(nil, nil),这时候还会报错
  • 当interface内没有任何成员时,元组会变为(nil, nil),但是不会报错
posted @ 2019-05-17 15:53  DickLai  阅读(353)  评论(0)    收藏  举报