Go 方法

Go 方法
wanghao
wanghao
357
阅读
0
评论
2021年11月08日17:23:22 0 357

Hi,我是行舟,今天和大家一起学习Go语言的方法。

在Go语言中方法是属于某个类型的函数,方法和函数相似,都是通过对一段代码逻辑的封装,达到重复调用的目的;但二者又有所不同:

函数和方法声明的方式不同。

函数可以被当作参数传递,方法则不行。

函数可以匿名,方法则不行。

接下来我们具体看下如何使用方法。

声明方法

func (t Type) 方法名(传入参数) (返回值) {

    方法体

}

和声明函数相比,声明方法需要在func后面添加Type类型的t,t可以在方法中被访问到。

基本用法

初始化

例1:

// 声明animal结构体

type animal struct {

   name string

   age int

   class string

   weight float32

}

// 声明animal类型的方法 getName

func (a animal) getName() {

   fmt.Printf("I am %s. \n" , a.name)

}

func main()  {

   a1 := animal{name:"Tom",age:3,weight:11.5,class:"猫"}

   a1.getName() // 调用animal结构体实例的方法

}

如上示例,我们声明了animal类型的结构体,并声明了animal类型的方法getName。然后声明了animal结构体的实例a1,a1就具有了animal的属性和方法。

方法不仅仅可以隶属于结构体类型,还可以隶属于非接口的其它任何自定义类型。

例2:

// 声明自定义类型test为go语言的基本类型int

type test int

// 声明 test类型的方法printTest

func (t test) printTest()  {

   fmt.Println("I am t",  t)

}

func main()  {

   var t test

   t = 10

   t.printTest() // print I am t 10

}

上面的例子中,我们使用关键词type定义了test类型,属于go语言的基本类型int;然后声明了test类型的方法printTest

自定义类型是使用 type关键词声明的数据类型,可以是结构体(struct)、基本数据类型、函数。如:type i int,type f func。

同一个类型的方法名称是不允许重复的,方法名和字段名之间也不允许重复,如果重复定义在编译期会报错。

函数代替方法

如上面的例1,我们也可以使用函数达到相同的效果。

例3:

// 声明animal结构体

type animal struct {

   name string

   age int

   class string

   weight float32

}

// 声明函数getName

func getName(a animal) {

   fmt.Printf("I am %s. \n" , a.name)

}

func main()  {

   a1 := animal{name:"Tom",age:3,weight:11.5,class:"猫"}

   getName(a1) // 调用getName函数

}

我们定义getName函数,函数的接收参数是animal类型,调用getName函数,传入a1,也达到了和例1相同的目的。

既然函数能达到和方法相同的目的,那为什么还要有方法呢?我认为主要有以下两个原因:

Go语言不是传统的面向对象的语言,它没有类的概念。通过结构体和方法可以加强Go语言面向对象的特性,模拟类的作用。https://golang.org/doc/faq#Is_Go_an_object-oriented_language

隶属于不同类型的方法可以重名,而函数不可以重名。

嵌套类型的方法

在结构体一节我们说到过,当结构体本身字段不存在时,会往被嵌套结构体的“深层”寻找。Go语言由浅入深逐层查找,找到了对应的字段就返回其值,并停止查找。对于方法也是相同的逻辑,Go语言会基于嵌套结构,由浅入深逐层查找,根据方法名调用对应的方法。

例4:

// 声明animalName结构体

type animalName struct {

   firstName string

   lastName string

}

// 声明animal结构体

type animal struct {

   animalName

   age int

   class string

   weight float32

}

func (an animalName) getAnimalName()  {

   fmt.Printf("My name is %s %s", an.firstName, an.lastName)

}

func main()  {

   a1 := animal{

      animalName: animalName{

         firstName:"tom",

         lastName:"steven",

      },

      age: 3,

      class:"猫",

      weight:12.5,

   }

   a1.getAnimalName() // print My name is tom steven

}

在上面的例子中,我们定义了animal结构体类型和嵌套的animalName结构体类型。给animalName类型定义了getAnimalName方法,在执行a1.getAnimalName()方法时,Go语言逐层查找到animalName类型的getAnimalName方法并调用。

值类型和指针类型

前面例子中我们声明的方法都属于值类型,方法还可以属于指针类型。

和函数的参数类型相似,值类型是值的副本,当我们在方法内修改副本的值时,如果是非引用类型就不会修改原值,如果是引用类型会修改原值;指针类型是指针地址的副本,所以我们在方法内的修改都会修改原值。

例5:

// 声明animalName结构体

type animalName struct {

   firstName string

   lastName string

}

// 声明animal结构体

type animal struct {

   animalName

   age int

   class string

   weight float32

}

func (an animalName) setAnimalName(firstName,lastName string)  {

   an.firstName =  firstName

   an.lastName = lastName

}

func (an *animalName) setAnimalNamePoint(firstName,lastName string)  {

   an.firstName =  firstName

   an.lastName = lastName

}

func main()  {

   a1 := animal{

      animalName: animalName{

         firstName:"tom",

         lastName:"steven",

      },

      age: 3,

      class:"猫",

      weight:12.5,

   }

   // print 修改前:a1.firstName=tom  a1.lastName=steven

   fmt.Printf("修改前:a1.firstName=%s  a1.lastName=%s \n", a1.firstName,a1.lastName)

   a1.setAnimalName("jerry","williams") // 修改a1的firstName和lastName

   // print 修改后:a1.firstName=tom  a1.lastName=steven

   fmt.Printf("修改后:a1.firstName=%s  a1.lastName=%s \n", a1.firstName,a1.lastName) 

   a2 := animal{

      animalName: animalName{

         firstName:"tom",

         lastName:"steven",

      },

      age: 3,

      class:"猫",

      weight:12.5,

   }

   // print 修改前:a2.firstName=tom  a2.lastName=steven

   fmt.Printf("修改前:a2.firstName=%s  a2.lastName=%s \n", a2.firstName,a2.lastName)

   a2.setAnimalNamePoint("jerry","williams") // 修改a2的firstName和lastName

   // print 修改后:a2.firstName=jerry  a2.lastName=williams 

   fmt.Printf("修改后:a2.firstName=%s  a2.lastName=%s \n", a2.firstName,a2.lastName)

}

如上示例,我们声明了animal结构体及其嵌套结构体animalName,然后声明了animalName结构体类型的两个方法:setAnimalName(值类型),setAnimalNamePoint(指针类型)。

调用setAnimalName方法修改a1的firstName和lastName,通过打印信息可以看出a1的firstName、lastName两个字段的值并没有被修改。

调用setAnimalNamePoint方法修改a2的firstName和lastName,通过打印信息可以看出a2的firstName、lastName两个字段的值被成功修改。

我们的例子中firstName和lastName都是string类型,如果是引用类型的话两种情况下值都会被修改。大家可以自行动手测试下。

细心的读者可能已经发现了,a2是值类型但是setAnimalNamePoint属于指针类型的方法,怎么还能调用成功呢?

这是因为Go语言帮我们做了自动转译,让我们通过值也可以调用指针类型的方法。上面例子中的a2.setAnimalNamePoint("jerry","williams") 就等价于(&a2).setAnimalNamePoint("jerry","williams")

我们何时使用值类型的方法何时使用指针类型的方法呢?

如果我们希望调用方法的对象本身也需要被改变时,我们可以考虑使用指针方法。

当类型特别复杂时我们为了防止过大的值拷贝,也可以使用指针方法。

其它情况可以使用值方法。

值类型和指针类型的自动转换

自定义数据类型的值仅仅包含了它所有的值方法,但是自定义数据类型的指针类型既包括了值方法,又包括了指针方法。因为调用指针方法时Go语言对值类型做了自动转译,所以这里不好举例验证,后面讲到接口时我们再举例证明。

总结

本文我们主要介绍了如何声明方法,方法的基本用法,方法和函数的区别和联系,值方法和指针方法的关系等内容。结构体和方法在Go语言中起到了类似其它语言类的概念,所以我们可以说Go语言是支持面向对象思维的编程语言。


打赏 点赞(0)
weinxin
投诉&咨询
文章名+链接地址,发送到此微信:tourism52
历史上的今天
06月
01
jsp,四大,作用域,有,什么,jsp,四大,作用域,有, 菜鸟教程

jsp四大作用域有什么

jsp四大作用域有什么 application:在所有应用程序中有效,即只要这个网站运行着,这个作用域就有效,这个指的程序的运行过程。 session:在当前会话中有效,即从浏览器...
类与,对象,常量 菜鸟教程

类与对象:类常量

类与对象:类常量可以把在类中始终保持不变的值定义为常量。在定义和使用常量的时候不需要使用 $ 符号。 常量的值必须是一个定值,不能是变量,类属性,数学运算的结果或函数调用...
PHP手册,影响,php,行为,扩展 菜鸟教程

影响 PHP 行为的扩展

影响 PHP 行为的扩展◾APC — Alternative PHP Cache(可选PHP缓存)◾简介◾安装/配置◾预定义常量◾APC 函数◾APCIterator — The ...
php,杂项,函数 菜鸟教程

PHP 杂项函数

PHP 杂项函数简介我们把不属于其他类别的函数归纳到这个页面。安装杂项函数是 PHP 核心的组成部分。无需安装即可使用这些函数。Runtime 配置杂项函数函数的行为受到 php....
Web 标准,网站标准 菜鸟教程

Web 标准

Web 标准,网站标准 Web 标准会帮助我们实现 WWW 的梦想。 Web 标准让 Web 开发更加容易。 为什么使用 web 标准? 由于存在不同的浏览器版本,web 开发者常...

评论列表 共有 0 条评论

暂无评论