约开头的成语
Golang中的Interface(接⼝),全⾯解析
哈哈镜子Go语⾔中的interface没有强制要求实现⽅法,但是interface是go中⾮常强⼤的⼯具之⼀。任⼀类型都可以实现interface中的⽅法,interface中的值可以代表是各种类型的值,这就是Go中实现多态的基础
什么是接⼝
interface就是字⾯意思——接⼝,C++中可以⽤虚基类表⽰;Java中就是interface。interface则是Golang更接近⾯向对象编程范式的另⼀个难点
interface是⽅法签名的⼀个集合,这些⽅法可以被任⼀类型通过⽅法实现。因此接⼝就是对象⾏为的申明(不是定义,仅仅表⽰⽅法签名,也可以称作函数原型)。
注意:我多次强调 任⼀类型 ,Golang中所有类型都可以实现⾃⼰的⽅法,为了便于理解,本⽂还是使⽤ struct(结构体)来做⽰例
举个例⼦ ,狗会⾛路也会汪汪叫,如果在⼀个接⼝中申明了Walk⽅法签名代表⾛路,Bark⽅法签名表⽰狗叫,然后我们有个结构体对象Dog,我们就说Dog实现了这个接⼝。
所以,接⼝的任务就是提供⽅法签名,⽅法签名由⽅法名,输⼊参数,返回值**三部分组成,任⼀类型可以实现这些⽅法,⽐如struct结构体。
如果你是⼀个OOP(⾯向对象)类型的程序员,你可能⽤过implement关键字来实现⼀个接⼝,但是在Golang中,你不需要明确的使⽤关键字来表⽰你实现了⼀个接⼝,如果你使⽤的类型实现了⼀个接⼝中的所有⽅法签名,那么Golang就默认你实现了这个接⼝。四有三者
注意:⼀个类型定义了⼀个或多个⽅法,⽅法的名字,参数,返回值和接⼝中的⽅法签名完全⼀致,并且接⼝中的所有⽅法签名在这个类型中都存在,那么我们称为实现了这个接⼝
举例⼦ ,如果我们说⼀个东西,⾛起来像鸭⼦,游泳像鸭⼦,并且叫起来像鸭⼦,那么在Golang中我们就认为它就是⼀只鸭⼦
!!
申明⼀个接⼝
类似struct的申明⽅法,我们需要使⽤interface关键字来定义类型别名来⽅便使⽤接⼝。乳扇
type Shape interface {
Area()  float64 //⾯积
Perimeter float64 //周长
}
在上⾯的例⼦中,我们定义了⼀个Shape接⼝,其中包含了两个⽅法签名:Area, Perimeter,⽆形参,返回float64值。任何类型,如果实现了和这两个签名形式⼀样*(同样的⽅法名,同样的传参,同样的返回值)*的⽅法,我们就称这个类型实现了Shape这个接⼝
由于接⼝ 是⼀种类似结构体 的类型,我们可以创建⼀个Shape类型的变量s。
上⾯的例⼦虽然简单,但是包含了很多信息,让我来理理接⼝ 的概念。接⼝ 包含两个类型(听上去有点奇奇怪怪)和⼀个值。
移动硬盘在电脑上显示不出来类型呢,⼀种是静态类型,另⼀种就是动态类型。静态类型就是指接⼝本⾝的类型,⽐如上图中的Shape 就是静态类型。
值只有动态值(没有静态值)。⼀个接⼝类型的变量只能表⽰实现了这个接⼝的动态类型的值。这个动态类型的变量就是这个接⼝的动态值。(ok,不知道有没有被我绕晕,晕了没事,这⾥做好笔记,继续往下看,回头再来理解这⾥的概念就明⽩了。)
从上⾯的例⼦中,我们可以发现,接⼝类型变量值和类型都是nil  。这是因为,我们这⾥声明的是Shape 变量,它还没有指定动态类型,更没有指定任何动态值。
当我们⽤fmt.Println函数的时候,它接受的就是接⼝类型的参数,第⼀个Println的参数就是指向了这个接⼝的动态值,第⼆个Println的参数指向的就是这个接⼝的动态类型。
事实上, 接⼝ s 是有个静态的类型的:Shape
接⼝的实现
接着上例⼦ ,我们先定义⼀个包含Area和Perimeter⽅法签名的Shape接⼝。然后我们创建⼀个实现了shape接⼝的结构体Rect(实现了以上两个⽅法)
雅瑰园
.
在上⾯的程序中,我们定义了⼀个Shape接⼝和Rect结构体。然后Rect实现了Area和Perimeter⽅法,这就实现了Shape接⼝的所有⽅法签名,所以我们就说Rect实现了Shape接⼝(这是Golang默认的,⾃动实现)。但是我们并没有明确的显式指明Rect实现了Shape接⼝,(如果是java语⾔则需要使⽤implement指明实现了某个接⼝,⽐如 public class Rect implement Shape)。
当⼀个类型实现了某个接⼝,这个类型的变量也可以⽤它所实现的接⼝类型表⽰(或者说⽤接⼝类型的变量去存放)。我们可以声明⼀个Shape接⼝类型的便令s ,然后⽤s 去接Rect类型的对象。(上图24,25⾏代码)
其实上⾯我们已经使⽤了多态的特性
因为Rect实现了Shape接⼝,所以第25⾏代码是完全有效的。我们可以看到,接⼝变量 s 的动态类型就是Rect,动态值就是Rect结构体对象 {5,4}
我们⽤动态这个词,是因为我们也可以给接⼝变量 s 赋值另⼀个实现了 Shape接⼝的结构体类型, 所以 s 实际指向的对象类型不是固定的,是动态的
有时候 接⼝ 的动态类型也叫做具体类型,因为我们获取接⼝类型的时候,它返回的是隐藏的动态值的类型,它的静态类型⼀直是隐式状态。
我们可以⽤ s 调⽤Area⽅法,因为Shape接⼝定义了Area⽅法并且 s 的具体类型Rect也实现了该⽅法。所以 s 调⽤的⽅法是动态对象的⽅法。
我们也可以⽐较变量 s 和 r 的值,因为此时他们动态的类型都是Rect类型,动态值都是 {5,4} 。
让我们改变 s 的动态类型和动态值: