go 修改结构体方法_Go36-13-结构体及其方法

结构体及其方法

结构体类型表示的是实实在在的数据结构。一个结构体类型可以包含若干个字段,每个字段通常都需要有确切的名字和类型。

结构体类型也可以不包含任何字段,这样并不是没有意义的,因为我们还可以为这些类型关联上一些方法,这里你可以把方法看做是函数的特殊版本。

方法和函数不同,它需要有名字,不能被当作值来看待,最重要的是,它必须隶属于某一个类型。方法所属的类型会通过其声明中的接收者(receiver)声明体现出来。

方法隶属的类型其实并不局限于结构体类型,但必须是某个自定义的数据类型,并且不能是任何接口类型。

方法的使用

接收者声明就是在关键字func和方法名称之间的那个圆括号包裹起来的内容,其中必须包含确切的名称和类型字面量。这个接收者的类型其实就是当前方法所属的那个类型,而接收者的名称,则用于在当前方法中引用它所属的类型的当前值。

举个例子:

// AnimalCategory 代表动物分类学中的基本分类法

type AnimalCategory struct {

kingdom string // 界

phylum string // 门

class string // 纲

order string // 目

family string // 科

genus string // 属

species string // 种

}

func (ac AnimalCategory) String() string {

return fmt.Sprintf(

"%s%s%s%s%s%s%s",

ac.kingdom,

ac.phylum,

ac.class,

ac.order,

ac.family,

ac.genus,

ac.species)

}

在Go语言中,我们可以通过为一个类型编写名为String的方法,来自定义该类型的字符串表示形式。这个String方法不需要任何参数声明,但需要有一个string类型的结果声明。所以在再用fmt包里的函数时,会打印出上面自定义的字符串表示形式,而无需显示的调用它的String方法。

我们可以把结构体类型中的一个字段看作是它的一个属性或者一项数据,再把隶属于它的一个方法看作是附加在其中数据之上的一个能力或者一项操作。将属性及其能力(或者说数据及其操作)封装在一起,是面向对象编程(object-orientedprogramming)的一个主要原则。

匿名字段

下面声明了一个结构体类型Animal,有两个字段,一个是string类型的scientificName。另一个字段声明中只有AnimalCategory,就是上面示例的那个结构体的名字:

type Animal struct {

scientificName string // 学名

AnimalCategory // 动物基本分类

}

字段声明AnimalCategory代表了Animal类型的一个嵌入字段。Go语言规范规定,如果一个字段的声明中只有字段的类型名而没有字段的名称,那么它就是一个嵌入字段,也可以被称为匿名字段。我们可以通过此类型变量的名称后跟“.”,再后跟嵌入字段类型的方式引用到该字段。也就是说,嵌入字段的类型既是类型也是名称。

强调一下,Go语言中没有继承的概念,它所做的是通过嵌入字段的方式实现了类型之间的组合。

简单来说,面向对象编程中的继承,其实是通过牺牲一定的代码简洁性来换取可扩展性,而且这种可扩展性是通过侵入的方式来实现的。类型之间的组合采用的是非声明的方式,我们不需要显式地声明某个类型实现了某个接口,或者一个类型继承了另一个类型。

同时,类型组合也是非侵入式的,它不会破坏类型的封装或加重类型之间的耦合。我们要做的只是把类型当做字段嵌入进来,然后坐享其成地使用嵌入字段所拥有的一切。如果嵌入字段有哪里不合心意,我们还可以用“包装”或“屏蔽”的方式去调整和优化。

值方法和指针方法

方法的接收者类型必须是某个自定义的数据类型(不能是接口)。所谓的值方法,就是接收者类型是非指针的自定义数据类型的方法。之前的示例中的方法都是值方法。

下面的这个就是指针方法:

func (a *Animal) SetScientificName(name string) {

a.scientificName = name

}

方法的接受者类型是*Animal,是一个指针类型。这时Animal可以被叫做*Animal的基本类型。可以认为,指针类型的值就是指向某个基本类型值的指针。指针方法,就是接收者类型是上述指针类型的方法。

值方法和指针方法之间的不同点:

值方法的接收者是方法所属类型的一个副本。在方法内对副本的修改一般不会提现在原值上,除非这个类型本身是某个引用类型。而指针方法内对的修改是一定会提现在原值上的。

严格来讲,通过值只能调用到值方法,通过指针只能调用到指针方法。但是,Go会适时的进行自动的转义,使得通过值也能调用到它的指针方法。比如,Animal.SetScientificName("Duck")会自动转义为(&Animal).SetScientificName("Duck"),即:先取指针值,然后再在改指针值上调用指针方法。

这条和接口相关,一个类型的方法集合中有哪些方法与它能实现哪些接口类型是息息相关的。如果一个基本类型和它的指针类型的方法集合是不同的,那么它们具体实现的接口类型的数量就也会有差异,除非这两个数量都是零。比如,一个指针类型实现了某某接口类型,但它的基本类型却不一定能够作为该接口的实现类型。

这个是验证上述差异的示例:

package main

import "fmt"

type Cat struct {

name string // 名字。

scientificName string // 学名。

category string // 动物学基本分类。

}

func New(name, scientificName, category string) Cat {

return Cat{

name: name,

scientificName: scientificName,

category: category,

}

}

func (cat *Cat) SetName(name string) {

cat.name = name

}

func (cat Cat) SetNameOfCopy(name string) {

cat.name = name

}

func (cat Cat) Name() string {

return cat.name

}

func (cat Cat) ScientificName() string {

return cat.scientificName

}

func (cat Cat) Category() string {

return cat.category

}

func (cat Cat) String() string {

return fmt.Sprintf("%s (category: %s, name: %q)",

cat.scientificName, cat.category, cat.name)

}

func main() {

cat := New("little pig", "American Shorthair", "cat")

cat.SetName("monster") // (&cat).SetName("monster")

fmt.Printf("The cat: %s\n", cat)

cat.SetNameOfCopy("little pig")

fmt.Printf("The cat: %s\n", cat)

type Pet interface {

SetName(name string)

Name() string

Category() string

ScientificName() string

}

_, ok := interface{}(cat).(Pet)

fmt.Printf("Cat implements interface Pet: %v\n", ok) // false

_, ok = interface{}(&cat).(Pet)

fmt.Printf("*Cat implements interface Pet: %v\n", ok) // true

}

这里牵涉到了接口的知识点,所以这个例子和下面的内容,下一篇还会再讲一遍。

最后的2行输出的内容,说明cat没有实现Pet的接口,而&cat是实现了Pet的接口。

因为要实现Pet接口需要实现接下的那4个方法。而Cat类型没有实现SetName方法,所以cat没有实现Pet接口。代码中SetName方法是通过*Cat实现的,另外其他的3个方法都已经通过Cat实现了,通过*Cat也能调用(差异的第2条),所以只有指针方法实现了Pet接口的所有方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/557947.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

iphone日历显示周视图_用敬业签记录放假安排 2021年放假安排日历

2021年已经踏着轻快的步伐向我们一路小跑而来,新的一年中,除了可以做更多的成绩之外,还有很多假期伴随而来。做好放假安排就可以让假期更加充实,为了能够更好的记录放假安排和相关事项可用敬业签这款便签软件。使用敬业签记录放假…

oracle删除orcl库_oracle删除数据文件

在我们详细介绍之前,我们必须说清楚一点:Oracle不提供如删除表、视图一样删除数据文件的方法,数据文件是表空间的一部分,所以不能“移走”表空间。一、使用offline数据文件的方法非归档模式使用:alter database datafi…

datagridview取消默认选中_winform datagridview中的 combobox如何选中默认值?

看了半天,我终于知道你是在添加行,并不是绑定——你是试图从数据库中逐行读取数据,然后赋值到Combobox中,对不?请不要这样做!请直接把DataTable作为数据源赋值给GridView!public partial class …

人工智能的炒作_为什么人工智能被过度炒作?

像任何新技术一样,人工智能也经历了称为“炒作周期”的各个阶段。它们从技术触发器开始。如果超过了人们设定的期望值(过度夸大),那么就会带来技术未达到预期的幻灭感,然后就是带来启发的成功案例,最后是生产力的稳定期。有了人工…

opencv轮廓周长原理_opencv轮廓逼近的精度参数与周长cvContourPerimeter的汇总

1)CV_Assert():若括号中的表达式值为false,则返回一个错误信息。函数原型是:#define CV_Assert( expr ) if((expr)) ; else cv::error( cv::Exception(CV_StsAssert, #expr, "", __FILE__, __LINE__) )2)cvContourPerimeter&#x…

Eclipse 导入项目乱码问题

Eclipse 导入项目乱码问题 1.编码不对 a.对某文件或某工程更改编码: 鼠标移到工程名或文件名,右键->Properties->Resource->Text file enCoding ->更改编码(GBK、UTF-8等)->Apply->OK退出 b.修改整个命名空…

mysql的所有聚合函数_MySQL 聚合函数(一)聚合(组合)函数概述

MySQL版本:5.7+ 一、MySQL 5.7中的聚合函数 MySQL 5.7中的聚合函数如下: 除非另有说明,否则组合函数会忽略NULL值。 如果在不包含Group By子句的语句中使用组合函数,就等效于对所有行进行分组。(个人理解是,结果总是只有一行。)关于这点的更多信息,后面的小节“MySQL处理…

mysql中的comment用法

mysql中的comment用法 在MySQL数据库中,字段或列的注释是用属性comment来添加。 创建新表的脚本中, 可在字段定义脚本中添加comment属性来添加注释。 示例代码如下: create table test( id int not null default 0 comment ‘用户id’ ) 如…

docker 安装mysql_Docker安装MySQL完整版流程

我们自己在linux上安装mysql的话,超级麻烦,安装完后还要进行各种各样的配置,之前在docker专题写了用docker安装mysql的快速版本以及修改编码的方式,这里总结一下安装流程和命令。环境Linux version 3.10.0-693.2.2.el7.x86_64dock…

mysql 转型_MySQL的未来在哪?

阿里云 MySQL&MariaDB 数据库产品结合开源社区,提供了稳定、可靠、便捷、弹性的在线数据库服务,帮助全球企业客户进行数字化转型。MySQL发展经历了一系列变化,从Sun到Oracle,发展也经过了几个阶段。MySQL从5.7版本开始走上了代…

mongoose换成mysql_如何将MongoDB数据库的数据迁移到MySQL数据库中

FAQ v2.0终于上线了,断断续续忙了有2个多月。这个项目是我实践的第一个全栈的项目,从需求(后期有产品经理介入)到架构,再到设计(有征询设计师的意见)、构建(前端、后台、数据库、服务器部署),也是第一次独立负责一个项目&#xff…

java虚拟机——JVM

JVM:java虚拟机,简称JVM,是运行所有java程序的假想计算机,是java程序的运行环境,是java最具吸引力的特征之一。我们编写的java代码,都运行在JVM之上。 跨平台:任何软件的运行,都必须…

mysql 5.0.37.tar.gz_Linux下MySQL5.0.37安装配置步骤

Linux下安装MySQL5.0.37需要以下面三个包:MySQL-client-community-5.0.37-0.rhel3.i386.rpmMySQL-server-community-5.0.37-0.rhel3.i386.rpmperl-DBI-1.53-2.fc7.i386.rpm(以下步骤需要root权限)1.验证是否已经安装过MySQLrpm -qa|grep MySQL如果发现有安装过,并需要卸载,使用…

JRE和JDK

JRE:是java程序的运行时环境,包含JVM和运行时所需要的核心类库。 JDK:时java程序开发工具包,包含JRE和开发人员使用的工具。 我们想要运行一个已有的java程序,那么只需要安装JRE即可。 我们想要开发一个全新的java程序…

mysql index subquery_[慢查优化]慎用MySQL子查询,尤其是看到DEPENDENT SUBQUERY标记时

它的执行计划如下,请注意看关键词“DEPENDENT SUBQUERY”:id select_type table type possible_keys key key_len ref rows Extra------ ------------------ ------ ------------…

java程序开发步骤

java程序开发步骤 开发环境搭建完毕后,可以开发第一个程序了 java程序开发三个步骤:编写,编译,运行。

mysql数据库rp集群_MySQL集群入门(PXC)

目标:1.掌握PXC集群MySQL方案的原理;2.掌握PXC集群的强一致性;3.掌握PXC集群的高可用方案;硬件要求:1.Win10x64企业版/linux/MacOS;2.Docker虚拟机;3.内存8GB以上;单节点数据库的弊端…

标识符、命名规则

标识符:是指在程序中,我们自己定义的内容。比如:类的名字、方法的名字和变量的名字等等; 命名规则:(硬性要求) 标识符可以包含英文字母26个(区分大小写),0-9…

ssh源码编译安装mysql_总结源码编译安装mysql

最近在学习源码编译安装LAMP、LNMP时,一直遇到一个难题,就是就是mysql无论怎么源码编译安装,到最后启动服务都提示"Starting MySQL.The server quit without updating PID file (/data/mysql/localhost.pid). [FAILED]&quo…

java中的常量

常量:在程序运行期间,固定不变的量。 常量的分类: 字符串常量:凡是用双引号引起来的部分,如:“abc”,“Hello”,“123”整数常量:直接写上的数字,没有小数点,如&#xf…