zoukankan      html  css  js  c++  java
  • Go与接口:实现接口的条件

    接口类型变量

    Go是强类型语言,你不能将整数值赋值给浮点型变量。同样,也不能将没有实现接口的类型值赋值给接口类型变量。

    // 1.定义变量是接口类型
    var w io.Writer
    // 2.将具体类型的值 赋值给这个变量, 要求这个具体类型实现了接口的所有方法
    w = os.Stdout
    w = new(bytes.Buffer)
    w = time.Second         // compile error: time.Duration 没有
    
    var rwc io.ReadWriteCloser
    rwc = os.Stdout
    rwc = new(bytes.Buffer)  // compile error: *bytes.Buffer lacks Close method
    
    
    // 等式右边是接口类型
    w = rwc
    rwc = w     // compile error: io.Writer lacks Close method
    

    关于一个类型持有一个方法的规则

    • T类型的值不持有*T指针的方法
    • *T指针的值持有T类型的方法

    所以可能会有*T指针值可以赋值为某接口类型变量而T类型的值不可以的情况

    type myWriter struct {}
    
    func (m *myWriter) Write(p []byte) (n int, err error) {
        return
    }
    
    func main() {
        var w io.Writer
        w = myWriter{}      // compile error: Cannot use 'myWriter{}' (type myWriter) as the type io.Writer Type does not implement 'io.Writer' as the 'Write' method has a pointer receiver
        w = &myWriter{}     // OK
    }
    

    接口类型封装和隐藏具体类型和它的值

    接口变量会限制自己只能调用接口类型声明的方法,即便该变量被赋予的值原本有其他方法和成员变量,也无法调用

    type myWriter struct {
        scope string
    }
    
    func (m *myWriter) Write(p []byte) (n int, err error) {
        return
    }
    
    func (m *myWriter) Func() {
    	return
    }
    
    func main() {
        my := &myWriter{"cn"}
        fmt.Println(my.scope)   // OK
        my.Func()               // Ok
        var w io.Writer
        w = my
        fmt.Println(w.scope)    // compile error: w.scope undefined
        w.Func()                // compile error: w.Func undefined
    }
    

    这样的限定就像是对实参进行了一层安全包装,函数内部只知道实参是接口类型,并不知道其他的信息,也无法访问。

    从之前的例子可以看到,大接口变量可以被赋值给小接口,被赋值后小接口也只能调用小接口的方法。

    空接口存在的意义

    空接口(interface{})即没有声明任何方法的接口。因为它什么都没有,所以它可以成为任何。

    package main
    
    import (
    	"bytes"
    	"fmt"
    )
    
    func emptyInterfaceIsAny() {
        var any interface{}
        any = true
        fmt.Println(any)
        any = 12.34
        fmt.Println(any)
        any = "hello"
        fmt.Println(any)
        any = map[string]int{"one": 1}
        fmt.Println(any)
        any = new(bytes.Buffer)
        fmt.Println(any)
        }
    

    这很有用,fmt.Println方法就是通过定义参数是空接口类型,才能接收任务类型的实参传递

    package main
    
    import "fmt"
    
    func printlnArgsIsEmptyInterface() {
    	fmt.Println(1, "hello", []string{"world"})  // 1 hello [world]
    }
    

    合理定义接口

    接口是对一些有着相同属性实体抽象。那对一些相同属性定义是仁者见仁智者见智,评判抽象的好与坏的标准即:这个接口类型的使用者是否舒服、被使用的次数多不对。
    文中展示的例子是对数字文化产品(如:音乐、电影和书籍)的抽象。

    其中对音乐和电影抽象出的Streamer值得思考

    type Streamer interface {
        Stream() (io.ReadCloser, error)
        RunningTime() time.Duration
        Format() string
    }
    

    每个具体类型基于他们实现接口声明的所有方法隐式地实现了接口。在Go语言中我们可以在需要的时候定义一个新的抽象或者特定特点的组,而不需要修改具体类型的定义。

  • 相关阅读:
    C#开源框架
    8 种 NoSQL 数据库系统对比
    安装补丁“此更新不适用于你的计算机”解决办法
    .net开源资料
    winform程序退出
    jquery.chained与jquery.chained.remote使用以及区别
    存储过程使用回滚
    C# Panel中绘图如何出现滚动条
    C#结构体的特点浅析
    如何用堆栈和循环结构代替递归调用--递归转换为非递归的10条军规
  • 原文地址:https://www.cnblogs.com/Zioyi/p/14845089.html
Copyright © 2011-2022 走看看