Go语言方法深入解析

目录方法定义匿名字段方法集表达式errorGolang方法总是绑定对象实例,并隐式将实例作为第一实参(receiver)。只能为当前包内命名类型定义方法。参数receiver可任意命名。如方法中未曾使用,可省略参数名。参数receiver类型可以是T或*

目录


Golang 方法总是绑定对象实例,并隐式将实例作为第一实参 (receiver)。

  • 只能为当前包内命名类型定义方法。
  • 参数 receiver 可任意命名。如方法中未曾使用 ,可省略参数名。
  • 参数 receiver 类型可以是 T 或 *T。基类型 T 不能是接口或指针。
  • 不支持方法重载,receiver 只是参数签名的组成部分。
  • 可用实例 value 或 pointer 调用全部方法,编译器自动转换。
    一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。

所有给定类型的方法属于该类型的方法集。

方法定义

package main
type Test struct{}

// 无参数、无返回值
func (t Test) method0() {}
// 单参数、无返回值
func (t Test) method1(i int) {}
// 多参数、无返回值
func (t Test) method2(x, y int) {}
// 无参数、单返回值
func (t Test) method3() (i int) {
  return
}
// 多参数、多返回值
func (t Test) method4(x, y int) (z int, err error) {
  return
}
// 无参数、无返回值
func (t *Test) method5() {
}
// 单参数、无返回值
func (t *Test) method6(i int) {
}
// 多参数、无返回值
func (t *Test) method7(x, y int) {
}
// 无参数、单返回值
func (t *Test) method8() (i int) {
  return
}
// 多参数、多返回值
func (t *Test) method9(x, y int) (z int, err error) {
  return
}
func main() {}  

//结构体
type User struct {
    Name  string
    Email string
}

//方法
func (u User) Notify() {
  fmt.Printf("%v : %v \n", u.Name, u.Email)
}
//方法
func (u *User) Notify() {
    fmt.Printf("%v : %v \n", u.Name, u.Email)
}
func main() {
  // 值类型调用方法
  u1 := User{"golang", "golang@golang.com"}
  u1.Notify()
  // 指针类型调用方法
  u2 := User{"go", "go@go.com"}
  u3 := &u2
  u3.Notify()
} 

普通函数与方法的区别

  1. 对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然。
  2. 对于方法(如struct的方法),接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以。

    
    package main
    //普通函数与方法的区别(在接收者分别为值类型和指针类型的时候)
    import (
    "fmt"
    )
    //1.普通函数
    //接收值类型参数的函数
    func valueIntTest(a int) int {
    return a + 10
    }
    //接收指针类型参数的函数
    func pointerIntTest(a *int) int {
    return *a + 10
    }
    func structTestValue() {
    a := 2
    fmt.Println("valueIntTest:", valueIntTest(a))
    //函数的参数为值类型,则不能直接将指针作为参数传递
    //fmt.Println("valueIntTest:", valueIntTest(&a))
    //compile error: cannot use &a (type *int) as type int in function argument
    
    b := 5
    fmt.Println("pointerIntTest:", pointerIntTest(&b))
    //同样,当函数的参数为指针类型时,也不能直接将值类型作为参数传递
    //fmt.Println("pointerIntTest:", pointerIntTest(b))
    //compile error:cannot use b (type int) as type *int in function argument
    }
    //2.方法
    type PersonD struct {
    id   int
    name string
    }
    //接收者为值类型
    func (p PersonD) valueShowName() {
    fmt.Println(p.name)
    }
    //接收者为指针类型
    func (p *PersonD) pointShowName() {
    fmt.Println(p.name)
    }
    func structTestFunc() {
    //值类型调用方法
    personValue := PersonD{101, "hello world"}
    personValue.valueShowName()
    personValue.pointShowName()
    
    //指针类型调用方法
    personPointer := &PersonD{102, "hello golang"}
    personPointer.valueShowName()
    personPointer.pointShowName()
    
    //与普通函数不同,接收者为指针类型和值类型的方法,指针类型和值类型的变量均可相互调用
    }

func main() { structTestValue() structTestFunc() }


## 匿名字段

Golang匿名字段 :可以像字段成员那样访问匿名字段方法,编译器负责查找。

通过匿名字段,可获得和继承类似的复用能力。依据编译器查找次序,只需在外层定义同名方法,就可以实现 “override”。
```go
package main
import "fmt"
type User struct {
  id   int
  name string
}
type Manager struct {
  User
  title string
}
func (self *User) ToString() string {
  return fmt.Sprintf("User: %p, %v", self, self)
}

func (self *Manager) ToString() string {
  return fmt.Sprintf("Manager: %p, %v", self, self)
}
func main() {
  m := Manager{User{1, "Tom"}, "Administrator"}
  fmt.Println(m.ToString())
  fmt.Println(m.User.ToString())
}  

方法集

Golang方法集 :每个类型都有与之关联的方法集,这会影响到接口实现规则。

  • 类型 T 方法集包含全部 receiver T 方法。
  • 类型 T 方法集包含全部 receiver T + T 方法。
  • 如类型 S 包含匿名字段 T,则 S 和 *S 方法集包含 T 方法。
  • 如类型 S 包含匿名字段 T,则 S 和 S 方法集包含 T + *T 方法。
  • 不管嵌入 T 或 T,S 方法集总是包含 T + *T 方法。

用实例 value 和 pointer 调用方法 (含匿名字段) 不受方法集约束,编译器总是查找全部方法,并自动转换 receiver 实参。

package main

import (
    "fmt"
)
type S struct {
    T
}
type T struct {
    int
}

func (t T) testT() {
    fmt.Println("类型 *T 方法集包含全部 receiver T 方法。")
}

func (t *T) testP() {
    fmt.Println("类型 *T 方法集包含全部 receiver *T 方法。")
}

func main() {
  t1 := T{1}
  t2 := &t1
  fmt.Printf("t2 is : %v\n", t2)
  t2.testT()
  t2.testP()

  s1 := S{T{1}}
  s2 := &s1
  fmt.Printf("s1 is : %v\n", s1)
  s1.testT()
  fmt.Printf("s2 is : %v\n", s2)
  s2.testT()
}

表达式

package main

import "fmt"
type User struct {
  id   int
  name string
}

func (self *User) TestPointer() {
  fmt.Printf("TestPointer: %p, %v\n", self, self)
}

func (self User) TestValue() {
  fmt.Printf("TestValue: %p, %v\n", &self, self)
}

func main() {
  u := User{1, "Tom"}
  fmt.Printf("User: %p, %v\n", &u, u)

  mv := User.TestValue
  mv(u)

  mp := (*User).TestPointer
  mp(&u)

  mp2 := (*User).TestValue // *User 方法集包含 TestValue。签名变为 func TestValue(self *User)。实际依然是 receiver value copy。
  mp2(&u)
} 

自定义error

抛异常和处理异常

package main
import "fmt"
// 系统抛
func test01() {
   a := [5]int{0, 1, 2, 3, 4}
   a[1] = 123
   fmt.Println(a)
   //a[10] = 11
   index := 10
   a[index] = 10
   fmt.Println(a)
}

func getCircleArea(radius float32) (area float32) {
   if radius < 0 {
      // 自己抛
      panic("半径不能为负")
   }
   return 3.14 * radius * radius
}
func test02() {
   getCircleArea(-5)
}
//
func test03() {
   // 延时执行匿名函数
   // 延时到何时?(1)程序正常结束   (2)发生异常时
   defer func() {
      // recover() 复活 恢复
      // 会返回程序为什么挂了
      if err := recover(); err != nil {
         fmt.Println(err)
      }
   }()
   getCircleArea(-5)
   fmt.Println("这里有没有执行")
}

func test04()  {
   test03()
   fmt.Println("test04")
}

func main() {
   test04()
}

抛异常和处理异常

package main
import (
   "errors"
   "fmt"
)
func getCircleArea(radius float32) (area float32, err error) {
   if radius < 0 {
      // 构建个异常对象
      err = errors.New("半径不能为负")
      return
   }
   area = 3.14 * radius * radius
   return
}

func main() {
   area, err := getCircleArea(-5)
   if err != nil {
      fmt.Println(err)
   } else {
      fmt.Println(area)
   }
}

自定义error

package main
import (
  "fmt"
  "os"
  "time"
)

type PathError struct {
  path       string
  op         string
  createTime string
  message    string
}

func (p *PathError) Error() string {
  return fmt.Sprintf("path=%s \nop=%s \ncreateTime=%s \nmessage=%s", p.path,
    p.op, p.createTime, p.message)
}

func Open(filename string) error {

  file, err := os.Open(filename)
  if err != nil {
      return &PathError{
        path:       filename,
        op:         "read",
        message:    err.Error(),
        createTime: fmt.Sprintf("%v", time.Now()),
      }
  }

  defer file.Close()
  return nil
}

func main() {
  err := Open("/Users/5lmh/Desktop/go/src/test.txt")
  switch v := err.(type) {
  case *PathError:
      fmt.Println("get path error,", v)
  default:

  }

}
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
天涯学馆
天涯学馆
0x9d6d...50d5
资深大厂程序员,12年开发经验,致力于探索前沿技术!