本文将介绍 Go 中的结构体、方法、接口、面向对象支持。
一、结构体
1. 结构体定义
1 2 3 4 5
| type 结构体名 struct { 成员名1 类型1 成员名2 类型2 ··· }
|
需要注意的是:
成员名可以省略,当省略时,成员将 “嵌入”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| type Person struct { Name string Phone string Addr string }
type Book struct { Title string Person ... ... }
book.Person.Name
book.Name
|
2. 结构体实例化
1 2 3 4 5 6 7 8 9 10 11
| var a 结构体名
var a = 结构体名{ 成员名1: 值1, 成员名2: 值2, ...}
var a = new(结构体名)
var a = &结构体名{}
|
3. 结构体成员访问
4. 构造函数
Go 语言并不支持直接在结构体中设置构造函数,通常我们会在同一个包下设置函数 NewXxx()
,以约定俗成的方式作为构造函数。
如果希望强制使用构造函数,可以将类型变为私有。
1 2 3 4 5 6 7
| type goods struct { name string }
func NewGoods(name string) *goods { return &goods{name} }
|
二、方法
1. 什么是方法?
方法与函数的区别在于,函数可以直接调用,而方法需要 “借助” 调用者调用。
2. 方法的定义
在 Go 语言中,只需要在函数之上额外设置接受者,函数便成为方法。
1 2 3
| func (接受者 接受者类型) 函数名([参数列表]) [返回值列表] { }
|
3. 方法接收者
接受者应该是命名类型的值或指针。
命名类型:通过类型定义所定义出来的类型
选择接收者类型时应该注意:
- 如果方法中的改动要反映到接收者之上,应该选择指针类型
- 如果接受者较大,考虑到值拷贝的开销,也可以选择指针类型
4. 方法值
1 2 3
| var 方法值 = 接受者.方法名
方法值()
|
5. 方法表达式
1 2 3
| var 方法表达式 = 接受者类型.方法名
方法表达式(接受者[, 参数1, ...])
|
三、接口
1. 什么是接口?
- 接口是一系列方法的集合
- 接口是隐式实现的,程序员不需要手动指定接口的实现与否,只要一个类型定义了接口中包含的所有方法,它就自动实现了该接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| type Phone interface { call() }
type NokiaPhone struct { }
func (nokiaPhone NokiaPhone) call() { }
type Iphone struct { }
func (iphone Iphone) call() { }
func main() { var phone Phone phone = new(NokiaPhone) phone.call() phone = new(Iphone) phone.call() }
|
2. 空接口
空接口的方法集合为空,这意味着任何类型都是对空接口的实现。因此,类型为空接口的形参可以接受任何值。
包括 Go 标准库在内的一些通用方法的实现,都使用了空类型作为数据元素类型。
3. 类型断言
一个接口可以被多个类型实现,接口类型的变量的具体类型是不确定的,可能是任何类型的。
因此,Go 提供了类型断言,类型断言会尝试将接口类型的变量转换为指定类型(或接口)。
1 2 3 4 5
| var i interface{} = 7 var a, ok = i.(int) if !ok { }
|
4. 反射
在 Go 中,反射可以在运行时访问变量的类型、值、方法等。
通过 reflect.TypeOf()
可以获取到任意值的类型对象,类型对象是 reflect.Type
类的实例,可以通过它获取到类型信息。
1 2 3
| var a int typeOfA := reflect.TypeOf(a) fmt.Println(typeOfA.Name(), typeOfA.Kind())
|
通过 reflect.ValueOf()
可以获取到任意值的值对象,值对象是 reflect.Value
类的实例,可以通过它获取到值信息。
1 2 3
| var a int valueOf := reflect.ValueOf(a) fmt.Println(valueOf.Int(), valueOf.Kind())
|
四、嵌入
1. 类型嵌入结构体
可以在定义结构体时嵌入类型,从而实现属性和方法的复用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| type Person struct { Name string Phone string Addr string }
type Book struct { Title string Person ... }
book.Person.Name
book.Name
|
2. 接口嵌入结构体
可以在定义接口体时嵌入接口,从而实现方法定义的复用。需要注意的是,接口体拥有该方法的定义,但是如果没有具体实现,调用时也会报错。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| type I interface { M1() M2() }
type T struct { I }
func (T) M1() { fmt.Println("M1") }
func (T) M3() { fmt.Println("M3") }
func main() { var t T t.M1() t.M2() t.M3() }
|
3. 接口嵌入接口
可以在一个接口的定义中嵌入另一个接口,该接口中将拥有被嵌入接口的所有方法,从而实现接口定义的复用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| type Reader interface { Read(p []byte) (n int, err error) }
type Writer interface { Write(p []byte) (n int, err error) }
type Closer interface { Close() error }
type ReadWriter interface { Reader Writer }
type ReadCloser interface { Reader Closer }
type WriteCloser interface { Writer Closer }
type ReadWriteCloser interface { Reader Writer Closer }
|
五、面向对象支持
面向对象有三个基本特征,Go 对它们的支持情况如下:
- 封装:在 Go 语言中,首字母大写的 “内容” 在包外可见,首字母小写的 “内容” 在包外不可见,可以借助这一特性实现封装
- 继承:Go 中并没有继承这一概念,但是可以通过组合(即内嵌)的方法实现能力的继承
- 多态:通过接口可以实现多态
参考