原创声明
做者: 刘丹冰Aceld, 微信公众号同名
如下代码能编译过去吗?为何?
package main import ( "fmt" ) type People interface { Speak(string) string } type Stduent struct{} func (stu *Stduent) Speak(think string) (talk string) { if think == "love" { talk = "You are a good boy" } else { talk = "hi" } return } func main() { var peo People = Stduent{} think := "love" fmt.Println(peo.Speak(think)) }
继承与多态的特色git
在golang中对多态的特色体现从语法上并非很明显。github
咱们知道发生多态的几个要素:golang
一、有interface接口,而且有接口定义的方法。数组
二、有子类去重写interface的接口。bash
三、有父类指针指向子类的具体对象服务器
那么,知足上述3个条件,就能够产生多态效果,就是,父类指针能够调用子类的具体方法。微信
因此上述代码报错的地方在var peo People = Stduent{}
这条语句, Student{}
已经重写了父类People{}
中的Speak(string) string
方法,那么只须要用父类指针指向子类对象便可。数据结构
因此应该改为var peo People = &Student{}
便可编译经过。(People为interface类型,就是指针类型)并发
如下代码打印出来什么内容,说出为何。
package main import ( "fmt" ) type People interface { Show() } type Student struct{} func (stu *Student) Show() { } func live() People { var stu *Student return stu } func main() { if live() == nil { fmt.Println("AAAAAAA") } else { fmt.Println("BBBBBBB") } }
结果负载均衡
BBBBBBB
分析:
咱们须要了解interface
的内部结构,才能理解这个题目的含义。
interface在使用的过程当中,共有两种表现形式
一种为空接口(empty interface),定义以下:
var MyInterface interface{}
另外一种为非空接口(non-empty interface), 定义以下:
type MyInterface interface { function() }
这两种interface类型分别用两种struct
表示,空接口为eface
, 非空接口为iface
.
空接口eface结构,由两个属性构成,一个是类型信息_type,一个是数据信息。其数据结构声明以下:
type eface struct { //空接口 _type *_type //类型信息 data unsafe.Pointer //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer相似于c语言中的void*) }
_type属性:是GO语言中全部类型的公共描述,Go语言几乎全部的数据结构均可以抽象成 _type,是全部类型的公共描述,type负责决定data应该如何解释和操做,type的结构代码以下:
type _type struct { size uintptr //类型大小 ptrdata uintptr //前缀持有全部指针的内存大小 hash uint32 //数据hash值 tflag tflag align uint8 //对齐 fieldalign uint8 //嵌入结构体时的对齐 kind uint8 //kind 有些枚举值kind等于0是无效的 alg *typeAlg //函数指针数组,类型实现的全部方法 gcdata *byte str nameOff ptrToThis typeOff }
data属性: 表示指向具体的实例数据的指针,他是一个unsafe.Pointer
类型,至关于一个C的万能指针void*
。
iface 表示 non-empty interface 的数据结构,非空接口初始化的过程就是初始化一个iface类型的结构,其中data
的做用同eface
的相同,这里再也不多加描述。
type iface struct { tab *itab data unsafe.Pointer }
iface结构中最重要的是itab结构(结构以下),每个 itab
都占 32 字节的空间。itab能够理解为pair<interface type, concrete type>
。itab里面包含了interface的一些关键信息,好比method的具体实现。
type itab struct { inter *interfacetype // 接口自身的元信息 _type *_type // 具体类型的元信息 link *itab bad int32 hash int32 // _type里也有一个一样的hash,此处多放一个是为了方便运行接口断言 fun [1]uintptr // 函数指针,指向具体类型所实现的方法 }
其中值得注意的字段,我的理解以下:
interface type
包含了一些关于interface自己的信息,好比package path
,包含的method
。这里的interfacetype是定义interface的一种抽象表示。type
表示具体化的类型,与eface的 type类型相同。 hash
字段实际上是对_type.hash
的拷贝,它会在interface的实例化时,用于快速判断目标类型和接口中的类型是否一致。另,Go的interface的Duck-typing机制也是依赖这个字段来实现。fun
字段实际上是一个动态大小的数组,虽然声明时是固定大小为1,但在使用时会直接经过fun指针获取其中的数据,而且不会检查数组的边界,因此该数组中保存的元素数量是不肯定的。因此,People拥有一个Show方法的,属于非空接口,People的内部定义应该是一个iface
结构体
type People interface { Show() }
func live() People { var stu *Student return stu }
stu是一个指向nil的空指针,可是最后return stu
会触发匿名变量 People = stu
值拷贝动做,因此最后live()
放回给上层的是一个People insterface{}
类型,也就是一个iface struct{}
类型。 stu为nil,只是iface
中的data 为nil而已。 可是iface struct{}
自己并不为nil.
因此以下判断的结果为BBBBBBB
:
func main() { if live() == nil { fmt.Println("AAAAAAA") } else { fmt.Println("BBBBBBB") } }
下面代码结果为何?
func Foo(x interface{}) { if x == nil { fmt.Println("empty interface") return } fmt.Println("non-empty interface") } func main() { var p *int = nil Foo(p) }
结果
non-empty interface
分析
不难看出,Foo()
的形参x interface{}
是一个空接口类型eface struct{}
。
在执行Foo(p)
的时候,触发x interface{} = p
语句,因此此时 x结构以下。
因此 x 结构体自己不为nil,而是data指针指向的p为nil。
ABCD中哪一行存在错误?
type S struct { } func f(x interface{}) { } func g(x *interface{}) { } func main() { s := S{} p := &s f(s) //A g(s) //B f(p) //C g(p) //D }
结果
B、D两行错误 B错误为: cannot use s (type S) as type *interface {} in argument to g: *interface {} is pointer to interface, not interface D错误为:cannot use p (type *S) as type *interface {} in argument to g: *interface {} is pointer to interface, not interface
看到这道题须要第一时间想到的是Golang是强类型语言,interface是全部golang类型的父类 函数中func f(x interface{})
的interface{}
能够支持传入golang的任何类型,包括指针,可是函数func g(x *interface{})
只能接受*interface{}
若是掌握interface
构造,建议看下一篇文章
使用Golang的interface接口设计原则
mail: danbing.at@gmail.com
github: https://github.com/aceld
原创书籍gitbook: http://legacy.gitbook.com/@aceld
(原创开源)Zinx-基于Golang轻量级服务器并发框架-完整版(附教程视频)
(原创开源)Lars-基于C++负载均衡远程调度系统-完整版