V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
zzyphp111
V2EX  ›  问与答

请教一个 golang 接口方法设计的问题

  •  
  •   zzyphp111 · 65 天前 · 969 次点击
    这是一个创建于 65 天前的主题,其中的信息可能已经有所发展或是发生改变。

    背景 :通过断言是怎么实现接口实例化注入

    疑问的关键代码:

    var _ log.Logger = (*stdLogger)(nil)
    

    提出疑问:

    首先有这样一个结构,表示 log 结构器

    
    // Logger is a logger interface.
    type Logger interface {
    	Log(level Level, keyvals ...interface{}) error
    }
    
    

    接下来按照正规流程,就是利用这个 logger 器,做些初始化 log 的方法

    
    var _ Logger = (*stdLogger)(nil)
    
    type stdLogger struct {
    	log  *log.Logger
    	pool *sync.Pool
    }
    
    // NewStdLogger new a logger with writer.
    func NewStdLogger(w io.Writer) Logger {
    	return &stdLogger{
    		log: log.New(w, "", 0),
    		pool: &sync.Pool{
    			New: func() interface{} {
    				return new(bytes.Buffer)
    			},
    		},
    	}
    }
    
    

    问题就在这里为什么,明明这个 new 方法返回的是 stdLogger 这个结构,而为啥返回类型是 Logger,我发现个特别点就是第一行做了断言处理,这个是什么原理,一般断言也是把已知结构转成明确固定结构,为啥这里明明 stdLogger 结构体和 Logger 结构体完全不一样,就可以实现断言?

    望大家不吝赐教,多多指出,或相关代码实现,本人对接口实现方面比较小白。

    14 条回复    2021-08-16 14:26:42 +08:00
    johnwood
        1
    johnwood   65 天前
    golang 的类型系统和 java 之类的不一样,他是鸭子类型系统,结构不用明确表明自己实现了某个接口,也没有 java 那样的“继承”。go 的倾向是组合优于继承。

    回到问题,估计因为 stdLogger 里有嵌入的 log *log.Logger
    zzyphp111
        2
    zzyphp111   65 天前
    zzyphp111
        3
    zzyphp111   64 天前
    @johnwood #1 这也是我所以疑问的,你看 zap 的引入就没有嵌入 log *log.Logger,但也断言成功了,这是为什么 https://github.com/go-kratos/kratos/blob/main/examples/log/zap.go
    gjquoiai
        4
    gjquoiai   64 天前
    一个编译时检查某个东西是否实现了某个接口的技巧,现在用的不多了,都用 linter 了
    johnwood
        5
    johnwood   64 天前
    @zzyphp111 你看#3 连接的 16 行 有嵌入自己的实现的 logger
    comwrg
        6
    comwrg   64 天前
    4L 说的对,是用来编译期保证正确的实现了某个接口,而不是等着运行之后程序 panic
    EscYezi
        7
    EscYezi   64 天前 via iPhone
    只要实现了 Logger 所有的方法就实现了 Logger 接口
    楼主提到的 stdLogger 和 zapLogger 可以断言成功是因为两个结构体都实现了 Logger 接口的 Log 方法,和内嵌的那个 log 成员没关系
    johnwood
        8
    johnwood   64 天前
    @EscYezi 你看 NewStdLogger 实例化的是什么
    EscYezi
        9
    EscYezi   64 天前 via iPhone
    @johnwood #8 实例化的 stdLogger 啊,stdLogger 实现了 Logger 接口
    EscYezi
        10
    EscYezi   64 天前
    可以尝试运行一下这段代码


    package main

    import "fmt"

    type Logger interface {
    Log(...interface{})
    }
    type myLogger struct {
    }

    func (l *myLogger) Log(...interface{}) {
    fmt.Println("my logger")
    }
    func main() {
    var l Logger = (*myLogger)(nil)
    l.Log("aaa")
    }
    johnwood
        11
    johnwood   64 天前
    @EscYezi 看了 2 楼的连接 你说的对 stdLogger 实现了 Logger 接口
    zzyphp111
        12
    zzyphp111   64 天前
    @EscYezi #10 嗯嗯,看了你的,感觉非常直观,也解决了我的疑问, 只要存在这个结构体的实现 就可以断言成功。

    感谢
    kksco
        13
    kksco   63 天前   ❤️ 1
    zzyphp111
        14
    zzyphp111   63 天前
    @kksco #13 非常感谢
    关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2531 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 13:56 · PVG 21:56 · LAX 06:56 · JFK 09:56
    ♥ Do have faith in what you're doing.