Go语言_通神路之五耀篇(1)

一、Go方法

灵胎篇博客中讲到函数,函数就是相似与java中的方法,然而go中还有一些升级版的函数,叫方法。java

只不过这种方法在方法名前还有一个括号加参数,只不过被称呼为接收者,方法名后面的括号没有参数,方法接收者在它本身的参数列表内,位于 func 关键字和方法名之间。函数

以下:这个就被称之为方法,由于方法名前面有接收者spa

package main

import (
   "math"
   "fmt"
)

type Person struct {
   x,y float64
}

func (p Person) Test() float64 {
   return math.Sqrt(p.x*p.x+p.y*p.y)
}

func main()  {
   value := Person{3,4}
   fmt.Print(value.Test())
}

反而,方法名后有参数,通常被称呼为函数,只不过是参数顺序不一样而已,调用的方式也不一样罢了指针

package main

import (
   "math"
   "fmt"
)

type Person struct {
   x,y float64
}

func Test(p Person) float64 {
   return math.Sqrt(p.x*p.x+p.y*p.y)
}

func main()  {
   value := Person{3,4}
   fmt.Print(Test(value))
}

不过这个接受者有必定的限制,对struct结构体类型没有限制要求,可是非结构体类型声明方法,必须是本包内!!!code

举例说明:blog

因此方法的接收者对结构体没有要求,非结构体如数据类型等都要求在同一个package下面才能够做为接收者。接口

二、指针接收者

    2.1 概念

在灵胎篇第五篇中讲到指针,& 操做符会生成一个指向其操做数的指针,* 操做符表示指针指向的底层值,所一当你运行下面的函数的时候,结果是50,开发

package main

import (
   "math"
   "fmt"
)

type Person struct {
   x,y float64
}

func (v *Person) Scale(f float64) {
   v.x = v.y * f
   v.y = v.y * f
}

func (p Person)Test() float64 {
   return math.Sqrt(p.x*p.x+p.y*p.y)
}

func main()  {
   value := Person{3,4}
   value.Scale(10)
   fmt.Print(value.Test())
}

当你去掉 Scale方法中接收者的指针的时候,结果会发生变化,由于去掉指针的话,执行那个函数的接收者将是Person结构体的一个副本,并非真正意义上的Person结构体,因此咱们须要用指针‘*’来更改博客

    2.2 方法与指针重定向

注意点:string

当函数的参数中有指针的状况下,

func ScaleFunc(v *Vertex, f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

这个状况下,函数中指针做为参数,必须用 类型前加&

ScaleFunc(v, 5)  // 编译错误!
ScaleFunc(&v, 5) // OK

而对于方法,能够忽略,以下:

func (v *Vertex) Scale(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}
var v Vertex
v.Scale(5)  // OK
p := &v
p.Scale(10) // OK

由于方法中的接收者v.Scale(5)自动转换(&v).Scale(5)操做

    2.3 方法与指针重定向(反向)

那说一下什么是反向,就是方法中的接收者和函数的参数不是指针了,而是正常类型,咱们接下来如何操做。也有要求

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func AbsFunc(v Vertex) float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

函数状况下:

var v Vertex
fmt.Println(AbsFunc(v))  // OK
fmt.Println(AbsFunc(&v)) // 编译错误!

方法状况下:

var v Vertex
fmt.Println(v.Abs()) // OK
p := &v
fmt.Println(p.Abs()) // OK

这种状况下,方法调用 p.Abs() 会被自动转换为(*p).ABs(),像极了java的自动装箱功能和int long的自动转换功能

因此咱们在开发中,为何方法用的很是多,函数用的不多的缘由,其次,这样能够避免在每次调用方法时复制该值。若值的类型为大型结构体时,这样作会更加高效。

三、接口

    3.1 概念理解

接口就是方法的集合,和java中的接口同样,都须要实现接口

package main

import (
   "math"
   "fmt"
)

type myFloat float64

type Base interface {
   test() float64
}

func (f myFloat)test() float64 {
   return math.Sqrt(float64(f))
}

func main()  {

   var base Base

   f := myFloat(25) 

   base = f  //f实现接口base

   fmt.Print(base.test())
}

接下来用指针的接收者操做下

package main

import (
   "math"
   "fmt"
)

type myFloat float64

type Base interface {
   test() float64
}

func (f *myFloat)test() float64 {
   return math.Sqrt(25)
}

func main()  {

   var base Base

   f := myFloat(25)

   base = &f

   fmt.Print(base.test())
}

一样是结果5,不过咱们上面不该该用数据类型做为指针接收者,由于须要用结构体,这样才方便在方法中使用操做业务逻辑。

接口接收者会实现接口!

    3.2 底层值为 nil 的接口值

package main

import "fmt"

type I interface {
    M()
}

type T struct {
    S string
}

func (t *T) M() {
    if t == nil {
        fmt.Println("<nil>")
        return
    }
    fmt.Println(t.S)
}

结果为:

(<nil>, *main.T)
<nil>
(&{hello}, *main.T)
hello

即使接口内的具体值为 nil,方法仍然会被 nil 接收者调用,在java中会触发一个空指针异常,但在 Go 中一般会写一些方法来优雅地处理它

*注意:* 保存了 nil 具体值的接口其自身并不为 nil。

    3.3 空接口

package main

import "fmt"

func main() {
    var i interface{}
    describe(i)

    i = 42
    describe(i)

    i = "hello"
    describe(i)
}

func describe(i interface{}) {
    fmt.Printf("(%v, %T)\n", i, i)
}

结果为:

(<nil>, <nil>)
(42, int)
(hello, string)

%V 意思是value %T是Type

相关文章
相关标签/搜索