Go语言 6 结构体、方法和接口
文章由作者马志国在博客园的原创,若转载请于明显处标记出处:http://www.cnblogs.com/mazg/
Go学习群:415660935
结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合。这些数据称为结构体的成员。Go语言的结构体和其他语言的类有同等的地位,但Go语言放弃了包括继承在内的大量面向对象特性,只保留了组合这个最基础的特性。这也体现了Go语言的简洁性。这章内容主要实现了Go语言的面向对象编程。
6.1.1 结构体定义
定义一个结构体相当于定义了一个新的类型。定义结构体的一般形式:
type 类型名称 struct { 成员列表 } |
例如,定义一个类型Person
type Person struct { name string age int } |
另外需要注意的是,结构体成员的输入顺序也有重要的意义。如果成员的顺序不同,意味着定义了不同的结构体类型。一个命令为S的结构体类型将不能再包含S类型的成员,因为一个聚合的值不能包含它自身(该限制同样适应于数组)但是S类型的结构体可以包含*S指针类型的成员,这可以创建递归的数据结构,例如链表和树结构。如果结构体没有任何成员的话,就是空结构体,写作struct{}.它的大小为0,也不包含任何信息。
6.1.2结构体初始化
结构体变量的的初始化可以按顺序直接初始化,也可以按照字段:值的方式初始化,这种方式的初始化对于字段的初始化的顺序可以是任意的。另外,也可以先定义结构体变量,随后赋值。
p1 := Person{"张三", 20} // 1 按顺序初始化 fmt.Println(p1) p2 := Person{age: 18, name: "李四"} // 2采用字段:值的方式初始化,可以任意顺序 fmt.Println(p2) p3 := Person{}// 3 未显示初始化,其成员默认被初始化为零值 fmt.Println(p3) p3.name = "王五" //给每个成员赋值 p3.age = 19 fmt.Println(p3) |
6.1.3 匿名组合实现继承
1 匿名组合的实现
只声明一个成员对应的数据类型而不指明成员的名字,这类成员就叫匿名成员。通过匿名成员实现的匿名组合,可以完成继承的功能。下面定义一个Student类,继承Person类的功能。匿名组合类型相当于以其类型名称(去掉包名部分)作为成员变量的名字。
type Person struct { name string age int } type Student struct { Person major string } func main() { s1 := Student{Person{"张三", 18}, "计算机应用"} fmt.Println(s1) fmt.Println(s1.Person) fmt.Println("姓名:", s1.Person.name, "年龄:", s1.Person.age, "专业:", s1.major) fmt.Println("姓名:", s1.name, "年龄:", s1.age, "专业:", s1.major) } //打印结果: {{张三 18} 计算机应用} {张三 18} 姓名: 张三 年龄: 18 专业: 计算机应用 姓名: 张三 年龄: 18 专业: 计算机应用 |
2匿名组合的重名问题
以下的示例中,Base1、Base2和Child名称相同。另外,匿名组合中Base2使用了指针类型。
type Base1 struct { name string } type Base2 struct { name string } type Child struct { Base1 *Base2 name string } func main() { c := Child{Base1{"base1"}, &Base2{"base2"}, "child"} fmt.Println(c) fmt.Println(c.name) fmt.Println(c.Base1.name) fmt.Println(c.Base2.name) } //打印结果: {{base1} 0xc0420082c0 child} child base1 base2 |
6.1.4 匿名结构
func main() { a := &struct { Name string Age int }{ Name: "zhangsan", Age: 18, } fmt.Println(a) b := struct{}{} fmt.Println(b) } //打印结果: &{zhangsan 18} {} |
6.1.5 结构体比较
如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的。可比较的结构体类型和其它可比较的类型一样,可以用于map的key类型。
6.2 方法
方法就是与某个类型(也可以是内置类型)关联的函数。理所当然我们可以给一个具体的结构体类型添加方法,使得它更像面向对象中的类。
6.2.1 方法声明
在函数声明时,在其名字之前放上一个变量,即是一个方法。相当于为这种类型定义了一个独占的方法。方法可以被声明到任意类型,只要这个类型本身不是一个指针或接口。只有类型和指向它们的指针才可以是接收器。如果一个类型名本身是一个指针的话,是不允许出现在接收器中的。
方法声明的语法格式如下:
func (变量名 类型)方法名称( [形参列表] ) [返回值列表]{ 方法体 } 或 func (变量名 *类型)方法名称( [形参列表] ) [返回值列表]{ 方法体 } |
接下来定义了一个新类型Integer,它和int没有本质不同,只是增加了个新方法Less()。
type Integer int func (a Integer) Less(b Integer) bool { return a < b } func main() { var i Integer = 2 fmt.Println(i.Less(3)) //调用对象i的Less方法 } |
在上面的例子中我们把Integer看着面向对象编程中的类,i是Integer类型的对象,Less()是对象的方法。如果需要修改对象i的值,这就需要使用指针作为接收器。
type Integer int func (a *Integer) Add(b Integer) { *a += b } func main() { var i Integer = 2 i.Add(3) fmt.Println(i) // 5 } |
不管你的方法的接收者是指针类型还是非指针类型,都可以通过指针/非指针类型进行调用,编译器会自动做类型转换。在实际的项目开发中,一般会约定如果某个类型有一个指针作为接收器的方法,那么所有该类型的所有方法都必须有一个指针接收器。
在声明一个方法的接收者该是指针还是非指针类型时,你需要考虑两方面:
1>对象本身是否特别大,如果声明为非指针变量时,调用会产生一次拷贝;
2>如果使用指针类型,需要注意这种指针类型指向的始终是一块内存地址,就算你对其进行了拷贝。
6.2.2 方法值和方法表达式
某个对象的方法称为方法值,某个类型的方法称为方法表达式。
type Integer int func (a Integer) Add(b Integer) Integer { return a + b } func main() { var i Integer = 2 add := i.Add //方法值:将对象i的方法作为值赋值给add变量 fmt.Println(add(3)) add2 := Integer.Add //方法表达式:将类型Integer的方法赋值给add2变量,add2的第一个参数作为接收器 fmt.Println(add2(i, 3)) } |
方法表达式可以根据变量来决定调用同一类型的不同方法。
type Integer int func (a Integer) Add(b Integer) Integer { return a + b } func (a Integer) Sub(b Integer) Integer { return a - b } func Operator(i, j Integer, sel bool) Integer { var op func(a, b Integer) Integer if sel { op = Integer.Add } else { op = Integer.Sub } return op(i, j) } func main() { fmt.Println(Operator(10, 5, true)) //15 fmt.Println(Operator(10, 5, false)) //5 } |
6.2.3 可见性
Go语言只有一种控制可见性的手段:首字母大写可见,小写字母则不可见。Go语言中符号的可访问性是包一级的而不是类型一级的。
package main import ( "fmt" ) type Person struct { Name string age int } func (p Person) Show() { fmt.Println("Name:", p.Name, "age:", p.age) } func (p *Person) setAge(age int) { p.age = age } func main() { p := Person{"张三", 18} p.Show() //包外也可见 p.age = 19 //包内可见,包外不可见 p.setAge(20) //包内可见,包外不可见 p.Show() } |
6.3 接口
6.3.1接口类型
接口类型是对其它类型行为的抽象和概括;因为接口类型不会和特定的实现细节绑定在一起。很多面向对象的语言都有类似的接口概念,但是Go语言中接口类型的独特之处在于它是满足隐式实现的。另外,一个接口也可以嵌入其它接口,即接口组合。
6.3.2 实现接口的条件
一个类型如果拥有一个接口需要的所有方法,那么这个类型就实现了这个接口。
// 1 定义Phone接口 type Phone interface { Call() } // 定义DumbPhone类型,并实现了Phone接口 type DumbPhone struct { //非智能手机 Name string } func (dp DumbPhone) Call() { fmt.Println("使用" + dp.Name + "非智能机打电话!") } // 2 定义Camera接口 type Camera interface { TakePicture() } // 定义DigitalCamera类型,并Camera接口 type DigitalCamera struct { //数码相机 Name string } func (d DigitalCamera) TakePicture() { fmt.Println("使用" + d.Name + "数码相机照相!") } // 3 定义PhoneCamera接口,嵌入了Phone和Camera接口 type PhoneCamera interface { Phone Camera } type CameraPhone interface {//与PhoneCamera接口等价,虽然顺序不同 TakePicture() Call() } // 3 定义SmartPhone类型,并实现了PhoneCamera接口 type SmartPhone struct { // 智能手机 Name string } func (sp SmartPhone) Call() { fmt.Println("使用" + sp.Name + "智能机手机打电话!") } func (sp SmartPhone) TakePicture() { fmt.Println("使用" + sp.Name + "智能手机照相!") } |
6.3.3 接口赋值
1 将一个对象赋值给一个接口
func main() { var p Phone p = &DumbPhone{Name: "诺基亚"} p.Call() p = &SmartPhone{Name: "华为"} p.Call() var c Camera c = &DigitalCamera{Name: "佳能"} c.TakePicture() c = &SmartPhone{Name: "华为"} c.TakePicture() var pc PhoneCamera pc = &SmartPhone{Name: "华为"} pc.Call() pc.TakePicture() } |
2 将一个接口赋值给另一个接口
在Go语言中,只要两个接口拥有相同的方法列表(次序不同不要紧),那么这两个接口是等价的,可以相互赋值。 接口PhoneCamera与接口CameraPhone等价。
var pc PhoneCamera pc = &SmartPhone{Name: "华为"} pc.Call() pc.TakePicture() var cp CameraPhone cp = pc //将一个接口赋值给另一个等价接口 cp.Call() cp.TakePicture() |
接口赋值并不要求两个接口必须等价。如果接口A的方法列表是接口B的方法列表的子集,那么接口B可以赋值给接口A。 接口Phone是接口PhoneCamera的子集,接口Camera也是接口PhoneCamera的子集。所以可以有如下的赋值:
var pc PhoneCamera pc = &SmartPhone{Name: "华为"} var p Phone var c Camera p = pc //父集赋值给子集 p.Call() c = pc //父集赋值给子集 c.TakePicture() |
6.3.4 接口查询
接口查询语法类似x.(T),x表示一个接口,T表示另一个接口类型。用来判断接口x操作的对象的是否实现了另一个接口类型。
var p Phone p = &SmartPhone{Name: "华为"} p.Call() if pc, ok := p.(PhoneCamera); ok { pc.TakePicture() } |
6.3.5 空接口
由于Go语言中任何对象实例都满足空接口interface{},所以interface{}看起来像是可
以指向任何对象的类型。一个函数把interface{}作为参数,那么他可以接受任意类型的值作为参数,如果一个函数返回interface{},那么也就可以返回任意类型的值。
6.3.6 类型查询
在Go语言中,还可以更加直截了当地询问接口指向的对象实例的类型。
//伪代码如下 var v1 interface{} = ... switch v := v1.(type) { case int: // 现在v的类型是int case string: // 现在v的类型是string ... } |
Go语言 6 结构体、方法和接口的更多相关文章
- Go语言 - 结构体 | 方法
自定义类型和类型别名 自定义类型 在Go语言中有一些基本的数据类型,如string.整型.浮点型.布尔等数据类型, Go语言中可以使用type关键字来定义自定义类型. 自定义类型是定义了一个全新的类型 ...
- go语言学习-结构体
结构体 go语言中的结构体,是一种复合类型,有一组属性构成,这些属性被称为字段.结构体也是值类型,可以使用new来创建. 定义: type name struct { field1 type1 fie ...
- Go语言中结构体的使用-第2部分OOP
1 概述 结构体的基本语法请参见:Go语言中结构体的使用-第1部分结构体.结构体除了是一个复合数据之外,还用来做面向对象编程.Go 语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性. ...
- C语言的结构体和C++结构体的区别
关于C++中声明结构体中需要使用构造器创建实例对象的语法: <C++的结构体构造方法的基本概念:结构体的构造方法需要和结构体的名字相同,并且无返回值,也不要void关键字,这样的方法就是构造器的 ...
- C语言中结构体对齐问题
C语言中结构体对齐问题 收藏 关于C语言中的结构体对齐问题 1,比如: struct{short a1;short a2;short a3;}A;struct{long a1;short a2;}B; ...
- 将c语言的结构体定义变成对应的golang语言的结构体定义,并将golang语言结构体变量的指针传递给c语言,cast C struct to Go struct
https://groups.google.com/forum/#!topic/golang-nuts/JkvR4dQy9t4 https://golang.org/misc/cgo/gmp/gmp. ...
- [日常] Go语言圣经--结构体,JSON习题
Go语言圣经-结构体 1.结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体 2.通常一行对应一个结构体成员,成员的名字在前类型在后,不过如果相邻的成员类型如果相同的话可以被合并到一行 ...
- Go语言中结构体的使用-第1部分结构体
1 概述 结构体是由成员构成的复合类型.Go 语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性.结构体成员,也可称之为成员变量,字段,属性.属性要满足唯一性.结构体的概念在软件工程上 ...
- C语言链表结构体(学习笔记)
#include <stdio.h> #define LENTEST 100 // 采取逐步删除的方法求的素数 //先假设1-100都是素数,然后剔除2的倍数, //3的倍数,直到剔除所有 ...
随机推荐
- Additinal Dependencies和#pragma comment(lib,"*.lib")的分析
网上.一些书上也写道,这两种方式作用一样.其实仔细分析,它们两者还是有非常大的差异的. Additinal Dependencies和#pragma comment(lib,"*.lib& ...
- 九度-题目1026:又一版 A+B
http://ac.jobdu.com/problem.php?pid=1026 题目描述: 输入两个不超过整型定义的非负10进制整数A和B(<=231-1),输出A+B的m (1 < m ...
- css 层加透明度后文字依然清晰
background: rgba(, , , !important; /*实现FF背景透明,文字不透明*/ filter: Alpha(opacity=); background: #0a0a0a; ...
- vue项目 axios封装第二弹
import axios from "axios"; import qs from "qs"; import { Message, MessageBox } f ...
- 【刷题】BZOJ 3926 [Zjoi2015]诸神眷顾的幻想乡
Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看. ...
- 【数组】- ArrayList自动扩容机制
不同的JDK版本的扩容机制可能有差异 实验环境:JDK1.8 扩容机制: 当向ArrayList中添加元素的时候,ArrayList如果要满足新元素的存储超过ArrayList存储新元素前的存储能力, ...
- 【BZOJ2460】元素(贪心,线性基)
[BZOJ2460]元素(贪心,线性基) 题面 BZOJ Description 相传,在远古时期,位于西方大陆的 Magic Land 上,人们已经掌握了用魔 法矿石炼制法杖的技术.那时人们就认识到 ...
- Classical Binary Search
Find any position of a target number in a sorted array. Return -1 if target does not exist. 与题目 Firs ...
- 洛谷 P3102 [USACO14FEB]秘密代码Secret Code 【区间dp】
农民约翰收到一条的消息,记该消息为长度至少为2,只由大写字母组成的字符串S,他通过一系列操作对S进行加密. 他的操作为,删除S的前面或者后面的若干个字符(但不删光整个S),并将剩下的部分连接到原字符串 ...
- [持续更新][备份]GDB调试工具常用命令
一.前言 ACM开赛在即,得知dev-cpp不适用之后,不得不再次重拾gdb基本操作... 辗转Emacs和Code::blocks数次之后,感觉还是Emacs更适合我的风格,尽管配置稍显麻烦,但其开 ...