• 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
    ", msg)
    	client.Close()
    }
    

    2、匿名接口和空接口

    2.1 匿名接口

    在定义变量时将类型指定为接口的函数签名的接口,此时叫匿名接口,匿名接口常用于初始化一次接口变量的场景

    //通过匿名接口声明接口变量
    var closer interface {
    	Close() error
    }
    closer = msn
    closer.Close()
    

    2.2 空接口

    不包含任何函数签名的接口叫做空接口,空接口声明的变量可以赋值为任何类型的变量(任意接口)

    • 空接口类型用interface{}表示,注意有{}
    • 空接口没有定义任何方法,因此任意类型都实现了空接口
    • func square(x interface{}){}该函数可以接收任意数据类型
    • slice的元素、mapkeyvalue都可以是空接口类型

    定义语法: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
    ", v, v)
    		case string:
    			fmt.Printf("String: %T %v
    ", v, v)
    		default:
    			fmt.Printf("Other: %T %v
    ", 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
    ", sender01, sender01, ok)  // *main.WechatSender, &main.WechatSender{ID:""}, true
    sender01.SendAll([]string{"张三", "李四"},"你好")
    if sender02, ok := ssender.(*WechatSender); ok {
    	fmt.Printf("%T, %#v, %v
    ", sender02, sender02, ok)  // *main.WechatSender, &main.WechatSender{ID:""}, true
    	fmt.Println(sender02.ID)
    }
    if sender03, ok := ssender.(*EmailSender); !ok {
    	fmt.Printf("%T, %#v, %v
    ", 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
    ", v)
    }
    

    利用断言判断数据类型

    package main
    
    import "fmt"
    
    func assert(i interface{})  {
    	switch v := i.(type) {
    	case int:  // v已被转为int类型
    		//v := i.(int)
    		fmt.Printf("%d
    ", v)
    		// 在 Type Switch语句的case子句中不能使用fallthrough
    	case float64:  // v已被转为float64类型
    		fmt.Printf("%f
    ", v)
    	case byte, uint16, string:  // 如果case后面跟多种type,则v还是interface{}类型
    		fmt.Printf("%T %v
    ", 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 ~

    关注公众号加群,更多原创干货与你分享 ~

  • 相关阅读:
    刷新页面的问题
    JS属性
    1-4_基本概念_进程间通信及同步
    1-3_基本概念_程序&进程
    1-2_基本概念_用户和组&目录、链接及文件
    1-1_基本概念_内核&shell
    d 操作系统概述
    对比生成测试算法用例
    简单的排序算法:冒泡排序法
    简单的排序算法:插入排序法
  • 原文地址:https://www.cnblogs.com/ssgeek/p/15476784.html
Copyright © 2020-2023  润新知