6.1Go方法

第六章 Go方法

第三章中讲解了struct,面向对象编程OOP已经是一个编程范式了,Go语言同样支持OOP开发。

一个对象就是一个变量,在这个对象中包含了一些方法,一个方法是一个和特殊类型关联的函数。

函数function,是一段具有独立功能的代码,可以反复被调用

方法method,是一个类的行为功能,只有该类的对象才可以调用

一个Person结构体,除了有基本的字段(姓名、年纪、性别…等等),Person结构体还应该有一些行为动作,如(吃饭、说话、跑步、学习等…),这些事需要定义方法去完成。

Go的方法是作用在指定的数据类型上的,与数据类型绑定,自定义类型都可以有方法,不仅仅是struct。隐式的将struct实例作为第一实参(receiver)。

可以为当前包内的任意类型定义方法。

package main
type X int
func (x *X)inc(){                //方法前的变量参数x称作 receiver,作用类似python的self
  *x++
}
func main(){
    var x X 
  x.inc()
  println(x)
}

1.1. 方法的声明和调用

package main

import "fmt"

//定义一个结构体数据类型
type Person struct {
    Username string
    Age      int
    Sex      string
}

//表示给Person结构体,绑定添加test方法
func (p Person) test() {
    fmt.Println("通过p变量,取出结构体类型中的Username值是:", p.Username)
}

func main() {
    p1 := &Person{
        "狗子",
        18,
        "男",
    }
    p1.test()
}

总结method

1.test方法和Person结构体类型绑定
2.test方法只能通过Person结构体的实例调用

语法

一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。

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

方法定义:

func (recevier type) methodName(参数列表)(返回值列表){}

参数和返回值可以省略
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() {}

自定义数据类型绑定方法

package main

import (
    "fmt"
)

type Integer int

func (i Integer) Print() {
    fmt.Println("i的值:", i)
}

func main() {
    var a Integer
    a = 1000
    a.Print()

    var b int = 200
    a = Integer(b)
    a.Print()
}

1.2. 方法实战

package main

import "fmt"

//定义一个结构体数据类型
type Person struct {
    Username string
    Age      int
    Sex      string
}

//此时这个(p Person)就是一个接受者
//Person结构体,人是可以说话的,添加speak方法
func (p Person) speak() {
    fmt.Printf("大声的喊出了自己的名字:%v
", p.Username)
}

//人还可以蹦跳
func (p Person) jump() {
    fmt.Printf("%v:跳起来一拳打在了姚明的膝盖上
", p.Username)
}

//人还可以进行算数
//方法的参数列表与返回值列表,与函数一致
func (p Person) getSum(n1, n2 int) int {
    sum := n1 + n2
    fmt.Printf("%v:飞快的计算出%d+%d的结果是%d
", p.Username, n1, n2, sum)
    return sum
}
func main() {
    p1 := &Person{
        "李二狗",
        18,
        "男",
    }
    p1.speak()
    p1.jump()
    res := p1.getSum(1, 2)
    fmt.Printf("p1.getSum方法返回值是%d
", res)
}

1.3. 方法使用细节

1)结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式

2)如程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理

3)当接受者不是一个指针时,该方法操作对应接受者的值的副本(意思就是即使你使用了指针调用函数,但是函数的接受者是值类型,所以函数内部操作还是对副本的操作,而不是指针操作。

package main

import "fmt"

type People struct {
    Name    string
    Country string
}

//此方法进行了值拷贝
func (p People) Print() {
    fmt.Printf("我是谁:name=%s country=%s
", p.Name, p.Country)
}

//此方法进行了值拷贝,不会对p1进行修改
func (p People) Set(name string, country string) {
    p.Name = name
    p.Country = country
}

//接收一个指针变量,可以修改原值
func (p *People) SetV2(name string, country string) {
    p.Country = country
    p.Name = name
}

func main() {
    var p1 People = People{
        Name:    "二狗子",
        Country: "沙河",
    }

    p1.Print()
    //此处修改无效,并没有修改p1的原地址
    p1.Set("二狗腿子", "日本")
    p1.Print()

    //两者效果一样,是被编译器进行了优化
    //(&p1).SetV2("people02", "english")
    p1.SetV2("狗官", "日本")
    p1.Print()
}

1.4. 方法和函数的区别

1.调用方式区别

函数调用:    函数名(参数列表)
方法调用:    变量名.方法名(参数列表)

2.对于普通函数,接受者为值类型时,不能将指针类型数据直接传递,传递时编译器就提示报错

package main

import "fmt"

func test(n1 int) int {
    n1 = n1 + 10
    return n1
}

func test2(n1 *int) int {
    *n1 = *n1 + 10
    return *n1
}

func main() {
    var num = 10
    //test函数接收的num有一个值拷贝的过程
    res := test(num)
    fmt.Println("值传递函数test结果:", res)
    fmt.Println("值传递函数test修改num的结果:", num)

    //test2函数接收num变量的地址,因此修改的也是num的值
    res1 := test2(&num)
    fmt.Println("指针传递函数test2结果:", res1)
    fmt.Println("值传递函数test2修改num的结果:", num)
}

3.对于方法(如struct的方法),接受者是值类型时,可以直接用指针类型变量调用方法,反之亦然。

package main

import "fmt"

type Person struct {
    Name string
}

func (p Person) test01() {
    p.Name = "码云"
    fmt.Printf("test01修改了name值:%v
", p.Name)
}

func (p *Person) test02() {
    p.Name = "麻花藤"
    fmt.Printf("test02修改了name值:%v
", p.Name)
}

func main() {
    var p1 Person = Person{
        "刘强东",
    }
    fmt.Println("p1默认值:", p1)

    //调用test01,由于值拷贝,并没有修改默认p1的Name值
    //(&p1).test01() 即使传入地址,仍然进行了值拷贝
    p1.test01()
    fmt.Printf("此时main程序调用p1.test01,此时p1.Name值:%v
", p1.Name)

    //传入地址
    //可以简写p1.test02(),修改的是p1.Name原本内存地址
    (&p1).test02()
    fmt.Printf("main程序调用p1.test02,此时p1.Name值%v
", p1.Name)
}
原文地址:https://www.cnblogs.com/open-yang/p/11256874.html