Go知识点之随便记记(五)— 接口

学习资料来自

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)最重要的三个方面

  • 封装(数据隐藏):分为了两个层次,包范围内(标识符首字母小写,仅在所在的包内可见)可导出的(标识符首字母大写,在所在包以外可见)
  • 继承(组合实现):内嵌一/多个想要的行为的类型;多重继承通过内嵌多个类型实现
  • 多态(接口实现):类型和接口松耦合,多重继承通过实现多个接口实现
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值