Go 语言 接口(Interface)

What is Interface type in Go ?

GoLang官网language specification文档对interface type的概念说明如下:

An interface type specifies a method set called its interface.
A variable of interface type can store a value of any type with a method set that is any superset of the interface.
Such a type is said to implement the interface. The value of an uninitialized variable of interface type is nil.

Go 语言提供了另外一种数据类型即接口(interface),它把所有的具有共性的方法定义在一起,这些方法只有函数签名,没有具体的实现代码(类似于Java中的抽象函数),任何其他类型只要实现了接口中定义好的这些方法,那么就说 这个类型实现(implement)了这个接口

接口的通用定义方式如下

/* 定义接口 */
type interface_name interface {
   method_name1 [return_type]
   method_name2 [return_type]
   method_name3 [return_type]
   ...
   method_namen [return_type]}

/* 定义xxx数据结构类型 */
type struct_name xxx

/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
   /* 方法实现 */}
    ...
func (struct_name_variable struct_name) method_namen() [return_type] {
   /* 方法实现*/}

上面使用type声明一个名为 interface_name 的接口,interface类型是可以定义为变量的,比如

var  name  interface_name

接口在Go语言中是引用类型,因此在上面定义的接口变量中,name是一个指针

我们依照上面的接口通用定义方式,定义以下实例

//定义电话接口
type Phone interface {
   call()
}
//自定义结构体
type Nokia struct {

}
//实现接口方法
func (nokia Nokia)call()  {
   fmt.Println("I am Nokia, I can call you!")
}

func main() {
   var  phone Phone
   phone = new(Nokia)
   phone.call()
}

call()是Phone接口定义好的一个方法,然后由Nokia实现了接口中这个方法,所以Nokia 实现了 Phone接口。

Interface“多态”特性实例
interface{}类型的变量,可以使用任何类型的值来赋值

func main() {
   var a interface{}  = 123
   var b interface{}  = "abc"
   var c interface{}  = 1.23
   fmt.Println(a,b,c)
}
-----output----
123 abc 1.23

在Go语言中自带的标准Packages中,有很多地方利用 interface 来处理未知数据类型,比如我们常用的fmt包,以fmt.Println为例,它的函数签名格式如下

// Println formats using the default formats for its operands and writes to standard output.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
func Println(a ...interface{}) (n int, err error) {
   return Fprintln(os.Stdout, a...)
}

fmt包的Println函数需要传入interface类型的可变长参数,该函数在实现底层的打印行为时,要求传入的可变长参数实现了fmt包中定义的Stringer接口。
Stringer接口类型描述如下:

// Stringer is implemented by any value that has a String method,
// which defines the ``native'' format for that value.
// The String method is used to print values passed as an operand
// to any format that accepts a string or to an unformatted printer
// such as Print.
type Stringer interface {
   String() string
}

所以,自定义类型想要调用fmt.Printf()做格式化打印,那只需实现Stringer接口就行。

举例说明:自定义输出类型格式
定义一个map集合,并循环输出map的元素

type PersonInfo struct {
   ID string
   Name string
   address string
}
func main() {
   //创建集合
   var myMap map[string] PersonInfo
   //初始化集合
   myMap = make(map[string]PersonInfo)
   //向map中添加元素
   myMap ["1"] = PersonInfo{"1","zhangsan","shanghai"}
   myMap ["2"] = PersonInfo{"2","wangwu","beijing"}
   myMap ["3"] = PersonInfo{"3","lisi","tianjin"}
   //循环输出
   for num,person := range myMap{
      fmt.Printf("%v: %v\n",num,person)
   }
}
----------output------------
1: {1 zhangsan shanghai}
2: {2 wangwu beijing}
3: {3 lisi tianjin}

现在我们要自定义map集合输出的格式,比如输出第一个元素:1: {1&zhangsan&shanghai},因此 PersonInfo 需要实现Stringer接口

//自定义类型需要实现Stringer接口
func (pInfo PersonInfo)String()string  {
   return fmt.Sprintf("%v&%v&%v",pInfo.ID,pInfo.Name,pInfo.address)
}

最终自定义格式化输出为:

1: 1&zhangsan&shanghai
2: 2&wangwu&beijing
3: 3&lisi&tianjin

接口嵌入
Go语言的接口对嵌入支持的非常好,接口可以嵌入其他的接口,效果就像在接口中 直接添加被嵌入接口的方法一样。

type HavingFoot interface {
   Foot()
}
type HavingEye interface {
   Eye()
}

type HuMan interface {
   HavingEye
   HavingFoot
}

HuMan接口嵌入了 HavingFoot 接口与 HavingEye 接口,就相当于HuMan接口包含了HavingFoot 接口与 HavingEye 接口中所有的方法。如果要实现HuMan接口,就要定义 Foot() 与 Eye() 方法。

类型猜测
如果有两个类型都实现了同一接口,那么这两个类型变量A,B都可以赋值给这个接口类型的变量C(类似于Java继承中的向上传递),然后这个接口类型的变量C,可以去查找是类型变量A还是B给它赋值的,查询通用格式如下:

value,ok := obj.(struct)

struct:需要猜测的类型A或B value:返回类型变量A或B
ok:查询结果 obj:接口类型的变量C

类型猜测实例

//定义电话接口
type Phone interface {
   call()
}
//自定义结构体
type Nokia struct {

}
type Iphone struct {

}

//实现接口方法
func (nokia Nokia)call()  {
   fmt.Println("I am Nokia, I can call you!")
}

func (iphone  Iphone)call()  {
   fmt.Println("I am Iphone, I can call you!")
}

func main() {
   //将两个子类赋值给接口
   var phone1 Phone = Nokia{}
   var phone2 Phone = Iphone{}
   //判断phone1是不是Nokia赋值给它的
   if values,ok := phone1.(Nokia);ok {
      values.call()
   }
   //判断phone2是不是Nokia赋值给它的
   if values,ok := phone2.(Iphone);ok {
      values.call()
   }
}

推荐阅读:https://studygolang.com/articles/2652

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容