接口

接口

Go语言中接口类型的独特之处在于它是满足隐式实现的. 也就是说, 我们没有必要对于给定的具体类型定义所有满足的接口类型; 简单地拥有一些必需的方法就足够了. 这种设计可以让你创建一个新的接口类型满足已经存在的具体类型却不会去改变这些类型的定义; 当我们使用的类型来自于不受我们控制的包时这种设计尤其有用.

接口约定

接口类型是一种抽象的类型. 它不会暴露出它所代表的对象的内部值的结构和这个对象支持的基础操作的集合; 它们只会展示出它们自己的方法. 也就是说当你看到一个接口类型的值时, 你不知道它时什么, 唯一知道的就是可以通过它的方法来做些什么.

一个类型可以自由的使用另一个满足相同接口的类型来进行替换被称作可替换性(LSP里氏替换).

接口类型

接口类型具体描述了一系列方法的集合, 一个实现了这些方法的具体类型是这个接口类型的实例.

新的接口类型可以通过组合已有的接口来定义.

实现接口的条件

一个类型如果拥有一个接口需要的所有方法, 那么这个类型就实现了这个接口.

interface{}被称为空接口类型. 因为空接口类型对实现它的类型没有要求, 所以我们可以将任意一个值赋给空接口类型.

判断是否实现了接口只需要比较具体类型和接口类型的方法, 所以没有必要在具体类型的定义中声明这种关系.

非空的接口类型比如io.Writer经常被指针类型实现, 尤其当一个或多个接口方法像Write方法那样隐式地给接受者带来变化的时候. 一个结构体的指针是非常常见的承载方法的类型.

flag.Value接口

调用flag.CommandLine.Var方法把标记加入到应用的命令行标记集合中.

接口值

概念上讲一个接口的值, 接口值, 由两个部分组成, 一个具体的类型和那个类型的值. 通常被称为接口的动态类型和动态值. 对于像Go语言这种静态类型的语言, 类型是编译期的概念: 因此一个类型不是一个值. 在我们的概念中, 用类型描述符提供每个类型的具体信息, 比如它的名字和方法. 对于一个接口值, 类型部分就用对应的类型描述符来表述.

Go语言中, 变量总是被一个定义明确的值初始化, 即使接口类型也不例外. 对于一个接口的零值就是它的类型和值的部分都是nil.

一个接口值基于它的动态类型被描述为空或非空. 可以通过==nil!=nil来判断接口值是是否为空. 调用一个空接口值上的任意方法都会产生panic.

一般来讲, 在编译时我们无法知道一个接口值的动态类型会是什么, 所以通过接口来做调用必须需要使用动态分发. 编译器必须生成一段代码来从类型描述符拿到名为write的方法地址, 在间接调用该方法地址. 调用的接收者就是接口的动态值.

接口值可以用==!=操作符来做比较. 如果两个接口值都是nil或者二者的动态类型完全一致且二者动态值相等(使用动态类型的==操作符来比较), 那么两个接口值相等. 应为接口值是可以比较的, 所以它们可以作为map的键, 也可以作为switch语句的操作数.

然而, 如果两个接口值的动态类型相同, 但是这个动态类型时不可比较的(比如切片), 将它们进行比较就是失败且panic.

考虑到这点, 接口类型时非常与众不同的. 其它类型那么是安全地可比较类型(如基本类型和指针)要么是完全不可比较的类型(如切片, 映射类型, 和函数), 但是在比较接口值或者包含了接口值的聚合类型时, 我们必须要意识到潜在的panic. 同样的风险也存在与使用接口作为map的键或者switch的操作数. 只能比较你非常确定的动态值是可比较类型的接口值.

警告: 一个包含nil指针的接口不是nil接口

空的接口值(其中不包含任何信息)与仅仅动态值为nil的接口值是不一样的.

sort.Interface接口

需要实现sort.Interface接口, 然后使用sort.Sort进行排序, 检查是否有序sort.IsSorted.

http.Handler接口

了解ServerMux.

error接口

error类型就是一个interface类型, 这个类型有一个返回错误信息的单一方法:

1
2
3
type error interface {
Error() string
}

类型断言

类型断言是一个使用在接口值上的操作. 语法它看起来像x.(T), 其中x是一个接口类型的表达式, 而T是一个类型(称为断言类型). 一个类型断言检查它操作对象的动态类型是否和断言的类型匹配.

这里有两种情况. 第一种, 如果断言的类型T是一个具体的类型, 然后断言类型检查x的动态类型是否和T相同. 如果检查成功了, 这个断言的结果是x的动态值, 当然的类型是T. 如果检查失败, 这个检查失败, 将会抛出panic.

第二种情况, 如果断言的类型T是一个接口类型, 然后断言检查是否x的动态类型满足T. 如果检查成功, 这个结果是一个有相同类型和值部分的接口值, 但是结果类型为T.

如果断言操作的对象是一个nil接口值, 那么不论被断言的类型是什么类型断言都会失败. 我们几乎不需要对一个更少限制的接口类型做断言, 因为它表现得就像赋值操作一样, 除了对于nil接口值的情况.

如果类型断言出现在一个预期有两个结果的赋值操作中, 这个操作不会在失败的时候发生panic, 但是用第二个布尔值来表示转换成功或失败. 失败时, 第一个值是被断言类型的零值.

基于类型断言区别错误类型

通过检查错误消息是否含有特定的子字符串从而来区分错误的类型是非常不成熟的. 一个更可靠的方式是使用一个专门的类型来描述结构化的错误.

类型开关

1
2
3
4
5
6
7
switch x.(type) {
case nil:
case int, uint:
case bool:
case string:
default:
}

常用的方法

1
2
3
4
5
# 从输入s中解析出一个变量. 
fmt.Sscanf()

# 字符串排序
sort.Strings()
-------------本文结束感谢您的阅读-------------