本文是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{}
表示,注意有{}
- 空接口没有定义任何方法,因此任意类型都实现了空接口
-
func square(x interface{}){}
该函数可以接收任意数据类型 -
slice
的元素、map
的key
和value
都可以是空接口类型
定义语法: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 ~
关注公众号加群,更多原创干货与你分享 ~