七叶笔记 » golang编程 » Golang之方法使用

Golang之方法使用

写在前面:

0x01 — 方法

在Golang中,方法的使用频率相当高,是重点需要掌握的组件功能,方法类似于对一个对象行为的实现,比如一只猫可以跑,可以跳,猫是主体,跑是猫的行为,一个函数,在其名字之前放上一个变量,即是一个方法。

 package main

import (
   "fmt"
   "testing"
)

// 定义结构
type Cat struct {
   name string
   age int
}
// 实现跑的方法
func(p Cat) Run() {
   fmt.Println(p.name + "在跑")
}
// 实现跳的方法
func(p Cat) Jump() {
   fmt.Println(p.name + "在跳")
}

// 给小猫年龄加一岁
func(p Cat) addAge() {
   p.age = p.age + 1
   fmt.Printf("%s的年龄增加1岁:%d \n", p.name, p.age)
}
// 给小猫年龄加a岁
func(p *Cat) addAgePtr(a int) {
   p.age = p.age + a
   fmt.Printf("%s的年龄增加%d岁:%d \n", p.name, a, p.age)
}
func TestMethod(t *testing.T) {
   cat1 := Cat{"小花", 2}
   fmt.Printf("猫名: %s ,年龄:%d \n", cat1.name,cat1.age)
   cat1.Run()
   cat1.Jump()

   cat1.addAge() // 给小花猫增加一岁
   // 我们在这里在打印一下小花猫的信息
   fmt.Printf("猫名: %s ,年龄:%d \n", cat1.name,cat1.age)

   cat1.addAgePtr(1) // 给小花猫增加一岁
   // 我们在这里在打印一下小花猫的信息
   fmt.Printf("猫名: %s ,年龄:%d \n", cat1.name,cat1.age)
}  

输出:

 === RUN   TestMethod
猫名: 小花 ,年龄:2 
小花在跑
小花在跳
小花的年龄增加一岁:3 
猫名: 小花 ,年龄:2 
小花的年龄增加1岁:3 
猫名: 小花 ,年龄:3 
--- PASS: TestMethod (0.00s)
PASS  

以上代码实现了简单的方法实现,方法就像是对主体的一种验证,成员就是主体的静态属性,方法是主体的动态属性,如果学过Java会对此比较熟悉。

方法的定义就是在函数名和func之间加上对象的主体,一是声明当前方法是属于哪个结构体,二是给方法提供一个结构体的调用对象,在方法中使用结构体的成员信息可以通过链式调用进行调取。

上面的示例中需要注意下,在定义方法时有两种结构体类型,一种是值类型,一种是指针类型,看代码示例的输出可以分析下不同,这里我直接说明,在使用值类型结构体作为参数时,在方法中对结构体成员进行操作,影响范围仅限于当前方法中,比如我们在执行addAge方法时,在方法内对age进行了+1,但是我们在执行完此方法后进行打印结构体的age时,发现age还是2,没有变化。

在执行方法addAgePtr时,我们定义的主体类型是指针型,方法中对age进行+a,在完成方法调用后,我们对结构体进行打印,可以看到age已经变化了,这是因为在使用指针作为类型传递时,会传递当前结构体的内存地址指针,在方法中做的修改,是对结构体本身进行的修改,而在使用值类型时,传递给方法的其实是结构体的复制。

在开发中需要注意区别,大部分情况我们会使用指针作为结构体参数传递给方法。

0x02 — 接口(interface)

接口是一种类型,它只定义了声明,没有具体实现,接口的作用是可以使你的代码从具体的实现中去耦,接口中可以定义很多声明,至于由哪个实体去实现,接口不关心,接口可以对某一类描述行为进行聚合,其定义是:

接口是一组 仅包含方法名、参数、返回值的未具体实现的 方法的集合

 package main

import (
   "fmt"
   "testing"
)

type animal interface {
   Run()
   Jump()
   eatMeat()
}

type Dog struct {
   name string
   age int
   eat string
}

// 实现跑的方法
func(d Dog) Run() {
   fmt.Println(d.name + "在跑")
}

// 实现跳的方法
func(d Dog) Jump() {
   fmt.Println(d.name + "在跳")
}

func(d Dog) eatMeat(){

   fmt.Println(d.name + "在吃肉")
}

func(d Dog) addAge(){
   d.age = d.age + 1
}


// 定义结构
type Cat struct {
   name string
   age int
}
// 实现跑的方法
func(c Cat) Run() {
   fmt.Println(c.name + "在跑")
}
// 实现跳的方法
func(c Cat) Jump() {
   fmt.Println(c.name + "在跳")
}

// 给小猫年龄加一岁
func(c Cat) addAge() {
   c.age = c.age + 1
   fmt.Printf("%s的年龄增加1岁:%d \n", c.name, c.age)
}
// 给小猫年龄加a岁
func(p *Cat) addAgePtr(a int) {
   p.age = p.age + a
   fmt.Printf("%s的年龄增加%d岁:%d \n", p.name, a, p.age)
}  

接口在使用中,接口后结构体没有强关联性,结构体可以实现一个接口的部分方法,也可以实现未在接口中定义的方法,同时多个结构体可以实现同一个方法,如果有结构体 实现了接口的所有方法,则认为实现了该接口,在开发工具中会对接口实现的结构体进行关联:

开发工具中的关联

如果实现了所有的接口,则可以通过工具方便查看接口所在位置,并追溯。

0x03 — new

在Golang中没有构造函数,可以自己实现一个构造函数,构造函数的目的在于初始化一个结构体,虽然没有构造函数,但是Golang中有new关键字,可以通过new关键字实现实例化一个结构体:

 dog1 := new(Dog)
dog2 := &Dog{}
dog3 := Dog{}  

dog1和dog2是等效的,实例化一个结构体并返回结构体的指针,dog3会实例化一个结构体,返回结构体本身。

0x03 — 总结

本篇学习了如何定义一个结构体的方法,如何对多个结构体相同的方法进行抽象成接口,创建方法时值传递还是指针传递做了对比,希望各位学者可以多多实践,起码文章中的示例要自己手敲一遍,多实践才能了解Golang开发者的意图,体会到Go语言奥妙。

相关文章