学习资料来自
GitHub - unknwon/the-way-to-go_ZH_CN: 《The Way to Go》中文译本,中文正式名《Go 入门指南》
1、接口(动态类型)
在各种动态类型的应用中,只要保证目标类型实现了接口的方法就可以进行各种类型的转换。
1)接口定义一组方法,方法不含实现代码。
2)接口内不能包含变量。
3)按照约定,接口通过以下方式命名:方法名+er后缀,也可以able结尾或I开头。
4)接口特性
(1)接口隐式实现,类型不需要显示声明;
(2)实现接口的类型除接口中方法外还可以有其他类型;
(3)一个类型可实现多个接口;
(4)接口类型可包含 一个 实例的(该实例的类型实现了该接口)引用。
5)总结一下,其实就是通过 var 定义一个接口后,该接口可以赋值任意实现了接口方法的对象,具体举例如下:
var r io.Reader
r = os.Stdin // see 12.1
r = bufio.NewReader(r)
r = new(bytes.Buffer)
f,_ := os.Open("test.txt")
r = bufio.NewReader(f)
6)接口嵌套:一个接口可以包含一个或多个其他接口。
7)因为接口是动态类型,可能被赋值多个不同的类型,所以常使用以下方式进行类型判断。
if v, ok := varI.(T); ok { // checked type assertion
Process(v)
return
}
// varI is not of type T
8)接口变量的类型可使用特殊形式的 type-switch 检测,但是在该检测中不允许存在 fallthrough
9)测试值是否实现了接口
// 测试值 v 是否实现了接口 Stringer
type Stringer interface {
String() string
}
if sv, ok := v.(Stringer); ok {
fmt.Printf("v implements String(): %s\n", sv.String()) // note: sv, not v
}
10) 接口上调用方法时,不正确的赋值在编译期就会失败
- 指针方法可以通过指针调用
- 值方法可以通过值调用
- 接收者是值的方法可以通过指针调用,因为指针会首先被解引用
- 接收者是指针的方法不可以通过值调用,因为存储在接口中的值没有地址
11)接口方法集满足如下调用规则
- 类型 *T 的可调用方法集包含接受者为 *T 或 T 的所有方法集
- 类型 T 的可调用方法集包含接受者为 T 的所有方法
- 类型 T 的可调用方法集不包含接受者为 *T 的方法
12)排序接口 Sorter :需实现三个方法 Len(),Less(i, j),Swap(i, j)
13)空接口 :不包含任何方法,对实现无要求,类似Java中的 Object,可对空接口类型的变量赋任何类型的值
(1)可使用空接口构建通用类型或包含不同类型变量的数组
type Element interface{}
type Vector struct {
a []Element
}
(2)复制数据切片到空接口切片,不能直接复制,需要单独一个一个进行赋值
(3)构建通用接口:使用空接口作为数据字段的类型(该字段可被赋值为任意类型)
14)反射包(用程序检查其所拥有的类型和变量,在运行时检查类型和变量,例如它的大小、方法和 动态
的调用这些方法)
(1)reflect.TypeOf 和 reflect.ValueOf :反射可以从接口值反射到对象,也可以从对象反射回接口值。reflect.Type 和 reflect.Value 都有许多方法用于检查和操作它们。一个重要的例子是 Value 有一个 Type 方法返回 reflect.Value 的 Type。另一个是 Type 和 Value 都有 Kind 方法返回一个常量来表示类型:Uint、Float64、Slice 等等。同样 Value 有叫做 Int 和 Float 的方法可以获取存储在内部的值(跟 int64 和 float64 一样)
(2)通过反射修改值
- 某些值不允许修改,可通过 CanSet() 进行测试、
- 如何通过反射修改不可修改的值,以 float64举例
-
// 传递一个 x 拷贝创建了 v,那么 v 的改变并不能更改原始的 x v := reflect.ValueOf(x) // v 的更改能作用到 x,那就必须传递 x 的地址 v = reflect.ValueOf(&x) //通过 Type() 看到 v 的类型是 *float64 并且仍然不可设置 // 想让其可设置我们需要使用 Elem() 函数 v = v.Elem() v.SetFloat(3.1415) //设置成功
(3) 反射结构
- NumField() 方法,返回结构内字段数量,可通过一个 for 循环用索引取得每个字段的值
15)Printf 中的反射
(1)fmt 包中的 Printf(以及其他格式化输出函数)都会使用反射来分析它的 ...
参数
func Printf(format string, args ... interface{}) (n int, err error)
16)动态类型(对象能做什么比它们是什么更重要。)
(1)Go 是唯一结合了接口值,静态类型检查(是否该类型实现了某个接口),运行时动态转换的语言,并且不需要显式地声明类型是否满足某个接口。
(2)接收一个(或多个)接口类型作为参数的函数,其实参可以是任何实现了该接口的类型的变量。
(3)借接口的特性实现重载功能:函数重载是不被允许的。可以用可变参数 ...T
作为函数最后一个参数来实现。把 T 换为空接口,那么可以知道任何类型的变量都是满足 T (空接口)类型的,这样就允许我们传递任何数量任何类型的参数给函数,即重载的实际含义。
17)结构体,集合,高阶函数
(1)定义结构体后,需要该结构体的指针对象集合,使用高阶函数,实际上也就是把函数作为定义所需方法(其他函数)的参数
type Any interface{}
type Car struct {
Model string
Manufacturer string
BuildYear int
// ...
}
type Cars []*Car
// Process all cars with the given function f
func (cs Cars) Process(f func(car *Car)) {
for _, c := range cs {
f(c)
}
}
18)总结
(1)面向对象:没有类,但是通过松耦合的类型、方法和对接口的实现来实现类的特性
(2)最重要的三个方面
- 封装(数据隐藏):分为了两个层次,包范围内(标识符首字母小写,仅在所在的包内可见)可导出的(标识符首字母大写,在所在包以外可见)
- 继承(组合实现):内嵌一/多个想要的行为的类型;多重继承通过内嵌多个类型实现
- 多态(接口实现):类型和接口松耦合,多重继承通过实现多个接口实现