Go编程基础——接口interface

接口interface
1、接 囗 是 一 个 或 多 个 方法 签 名 的 集 合
2、 只 要 某 个 类 型 拥 有 该 接 囗 的 所 有 方 法 签 名 , 即 算 实 现 该接口, 无 需 显式
声 明 实 现 了 哪 个 接 囗 , 这 称 为 Structural Typing
3、接 囗 只 有 方法 声 明 , 没 有 实 现 , 没 有数据字段
4、接 囗 可 以 匿 名 嵌 入 其 它 接 囗 , 或 嵌 入结构中
5、将 对 象 赋 值 给 接 口 时 , 会 发 生 拷 贝 , 而 接 口 内 部 存 储 的 是 指 向 这 个
复 制 品 的 指 针 , 既无法修 改 复 制 品 的 状 态, 也 无法获取指针(换句话说就是值拷贝)
6、只 有 当 接 囗 存 储 的 类 型 和 对 象 都 为 nil 时 , 接 囗 才 等 于nil
7、接 囗 调 用 不 会 做 receiver自动转换
8、接 囗 同 样 支 持 匿 名 字 段 方 法
9、接 囗 也 可 实 现 类 似 OOP 中 的 多 态
10、空 接 囗 可 以 作 为 可 类 型 数 据 的 容 器
首先来一个案例包括定义一个接口,一个结构,使用这个结构实现前边定义接口中声明的方法。

type USB interface {
   Name() string
   Connect()
}
type TelephoneConnector struct{
   name string
   brand string
}
func (tc TelephoneConnector) Name() string{
   return tc.name
}
func (tc TelephoneConnector) Connect() {
   fmt.Println("Connect:",tc.name)
}

func disconnect(usb USB){
   fmt.Println("disconnect is success!")
}
func main() {
   var a USB
   a=TelephoneConnector{"TelephoneConnector",
   "MeiZu"}
   a.Connect()
   disconnect(a)
}

运行结果

Connect: TelephoneConnector
disconnect is success!

上边的程序首先是定义了一个接口USB,其中声明了两个方法的签名Name() string和Connect,接着定义了一个结构Telephone,在结构中定义了两个字段name和brand,这样Telephone是一个类型,也就有了main方法中定义一个变量a为USB,这里就需要初始化变量的值(因为定义的两个字段都是string,不初始化值不容易看出程序的运行过程。)
按照USB接口中定义方法签名以及对返回值的声明来构建两个方法。第一个参数是receiver的类型(结构类型),其实这一步也就在告诉编译器,这个接收器(对应的结构)实现对应的接口。
主函数中a变量可以使用

a:=Telephone{"TelephoneConnector",
   "MeiZu"}
var a USB
   a=TelephoneConnector{"TelephoneConnector",
   "MeiZu"}

两种方式,第一种不容看出它是否实现了USB,所以就有了接下来disconnect(usb USB)方法穿入a,能够成功的实现也就是说明了a实现了USB接口。其实变量a还可以使用

var a TelephoneConnector
   a=TelephoneConnector{"TelephoneConnector",
   "MeiZu"}

毕竟TelephoneConnector是USB接口的具体实现,直接定义为TelephoneConnector类型也是可以的。
嵌入接口(在Java中的接口的多继承的关系——为了灵活性)
嵌入接口与嵌入接口一样的实现方式,深刻地用到Go语言中重要的概念——组合
这里要注意的组合不同嵌入接口的时候,外层接口和嵌入接口定义的方法签名一致的话,外层接口的同名方法签名会报错

duplicate method 'Connect' less... (Ctrl+F1)
Reports names redeclared in this block.
type USB interface {
   Connector
   Name() string
}
type Connector interface {
   Connect()
}

type TelephoneConnector struct{
   name string
   brand string
}
func (tc TelephoneConnector) Name() string{
   return tc.name
}
func (tc TelephoneConnector) Connect() {
   fmt.Println("Connect:",tc.name)
}

func disconnect(usb USB){
   fmt.Println("disconnect is success!")
}
func main() {
   var a USB
   a=TelephoneConnector{"TelephoneConnector",
   "MeiZu"}
   a.Connect()
   disconnect(a)
}

运行结果

Connect: TelephoneConnector
disconnect is success!

成功运行,这里是使用Connector接口嵌入在USB接口中。
其实这里的问题是如何知道调用disconnect()方法是Telephone,毕竟实现USB接口可能不止是Telephone这个结构,同时这里的usb类型是无法调用Telephone中的name字段的。这就需要改装disconnect方法来实现我们的猜想。
利用ok parse的表达方式来实现是一个常用的技巧。

func disconnect(usb USB){
   if tc,ok:=usb.(TelephoneConnector);ok{
      fmt.Println("func in disconnect:",tc.name)
      return
   }
   fmt.Println("unknown type!")
}

运行结果

Connect: TelephoneConnector
func in disconnect: TelephoneConnector

这里还有两点要注意的,用一个接口判断一个结构是否属于该接口的方法类似usb.(TelephoneConnector);if判断之后记得要添加上return不然程序会继续执行之后的程序。
最广泛的接口是空接口,interface{}
改造上边的disconnect()方法,这样实现空接口也是一样可以的,并不影响之前的运行结果。

func disconnect(usb interface{}){
   if tc,ok:=usb.(TelephoneConnector);ok{
      fmt.Println("func in disconnect:",tc.name)
      return
   }
   fmt.Println("unknown type!")
}
func disconnect(usb interface{}){
   switch tc:=usb.(type) {
   case TelephoneConnector:
      fmt.Println("func in disconnect:",tc.name)
   default:
      fmt.Println("unknown type!")

   }
}

接口之间实现转化
强制类型转换,和类型的大小有关。

tel:=TelephoneConnector{"TelephoneConnector",
   "MeiZu"}
var a Connector
a=Connector(tel)
a.Connect()
disconnect(a)

这时候a没有name属性。

类 型 断 言
通 过 类 型 断 言 的 ok pattern 可 以 判 断 接 囗 中 的 数 据 类 型
使用type switch 则 可 针 对 空 接 囗 进 行 比 较 全 面 的 类 型 判 断
接 囗 转 换
- 可 以 将 拥 有 超 集 的 接 口转换为子集的接 囗

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章