分享
  1. 首页
  2. 文章

Go 语言 接口(Interface)

小杰的快乐时光 · · 4423 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:小杰的快乐时光

查看原文:Go 语言 接口(Interface)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
4423 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏