Golang 接口類型 - 下篇

目錄

本文是 Golang 接口類型 - 上篇 的續篇內容

1、接口嵌入

和結構體struct一樣,接口之中也可以嵌入已存在的接口,從而實現接口的擴展

1.1 定義

// Sender 定義Sender接口
type Sender interface {
 Send(msg string) error
}

// Receiver 定義Receiver接口
type Receiver interface {
 Receive() (string, error)
}

// Client Client,由Sender和Receiver組合
type Client interface {
 Sender  // 匿名嵌入
 Receiver  // 匿名嵌入
 Open() error
 Close() error
}

1.2 實現

// MSNClient 定義MSNClient結構體,並實現Client接口中Open/Send/Receive/Close方法
type MSNClient struct{
}

func (c MSNClient) Open() error {
 fmt.Println("Open")
 return nil
}

func (c MSNClient) Close() error {
 fmt.Println("Close")
 return nil
}

func (c MSNClient) Send(msg string) error {
 fmt.Println("send:", msg)
 return nil
}

func (c MSNClient) Receive() (string, error) {
 fmt.Println("Receive")
 return "", nil
}

1.3 使用

func main() {
 //msn := MSNClient{}
 //var s Sender = msn
 //var r Receiver = msn
 //var c Client = msn
 //s.Send("1")
 //r.Receive()
 //c.Open()
 //defer c.Close()
 //c.Send("2")
 //c.Receive()
 var client Client = MSNClient{}
 client.Open()
 client.Send("Hi")
 msg,_ := client.Receive()
 fmt.Printf("%q\n", msg)
 client.Close()
}

2、匿名接口和空接口

2.1 匿名接口

在定義變量時將類型指定爲接口的函數簽名的接口,此時叫匿名接口,匿名接口常用於初始化一次接口變量的場景

//通過匿名接口聲明接口變量
var closer interface {
 Close() error
}
closer = msn
closer.Close()

2.2 空接口

不包含任何函數簽名的接口叫做空接口,空接口聲明的變量可以賦值爲任何類型的變量(任意接口)

定義語法:interface{}

package main

import "fmt"

type EStruct struct {
}

type Empty interface {
}

func main() {
 es := EStruct{}
 var e interface{} = 1
 fmt.Println(es, e)  // {} 1
 e = "test"
 fmt.Println(e)  // test
 e = true
 fmt.Println(e)  // true
 e = es
 fmt.Println(e)  // {}
}

2.3 使用場景

聲明函數參數類型爲interface{},用於接收任意類型的變量

package main

import "fmt"

type EStruct struct{
}

func printType(args ...interface{}) {
 fmt.Println("------------------------")
 for _, arg := range args {
  //fmt.Println(arg)
  switch v := arg.(type) {
  case int:
   fmt.Printf("Int: %T %v\n", v, v)
  case string:
   fmt.Printf("String: %T %v\n", v, v)
  default:
   fmt.Printf("Other: %T %v\n", v, v)
  }
 }
}

func main() {
 es := EStruct{}
 printType(1, "test", true, es)
 /*
 Int: int 1
 String: string test
 Other: bool true
 Other: main.EStruct {}
  */
}

3、接口斷言和查詢

類型賦值成了接口類型,能否通過某種方式轉換成當時賦值的類型呢?

當父集接口或者類型對象賦值給接口變量後,需要將接口變量重新轉換爲原來的類型,需要使用類型斷言 / 查詢

3.1 斷言

語法:接口變量.(Type)判斷一個接口能否轉換成具體類型

// 使用類型斷言信息轉換
sender01, ok := ssender.(Sender)
fmt.Printf("%T, %#v, %v\n", sender01, sender01, ok)  // *main.WechatSender, &main.WechatSender{ID:""}true
sender01.SendAll([]string{"張三""李四"},"你好")
if sender02, ok := ssender.(*WechatSender); ok {
 fmt.Printf("%T, %#v, %v\n", sender02, sender02, ok)  // *main.WechatSender, &main.WechatSender{ID:""}true
 fmt.Println(sender02.ID)
}
if sender03, ok := ssender.(*EmailSender); !ok {
 fmt.Printf("%T, %#v, %v\n", sender03, sender03, false)  // *main.EmailSender, (*main.EmailSender)(nil)false
}

3.2 查詢

可以通過switch-case+接口變量.(type)查詢變量類型,並選擇對應的分支塊

// 使用類型查詢
sender = &EmailSender{"test"}
switch v := sender.(type) {
case EmailSender:
 fmt.Println("EmailSender", v.SmtpAddr)
case *EmailSender:
 fmt.Println("*EmailSender", v.SmtpAddr)  // *EmailSender test
case *SmsSender:
 fmt.Println("*SmsSender", v.SmsAPI)
case *WechatSender:
 fmt.Println("*WechatSender", v.ID)
default:
 fmt.Printf("error, %#v\n", v)
}

利用斷言判斷數據類型

package main

import "fmt"

func assert(i interface{})  {
 switch v := i.(type) {
 case int:  // v已被轉爲int類型
  //v := i.(int)
  fmt.Printf("%d\n", v)
  // 在 Type Switch語句的case子句中不能使用fallthrough
 case float64:  // v已被轉爲float64類型
  fmt.Printf("%f\n", v)
 case byte, uint16, string:  // 如果case後面跟多種type,則v還是interface{}類型
  fmt.Printf("%T %v\n", i, i)
 }
}

func main()  {
 var i interface{}
 var a int
 var b float64
 var c byte
 i = a
 assert(i)  // 0
 i = b
 assert(i)  // 0.000000
 i = c
 assert(i)  // uint8 0
}

See you ~

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/9O1npLvL7hGC40uH7y12pg