zzyphp111
V2EX  ›  问与答

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

  •  
  •   zzyphp111 · Aug 14, 2021 · 2720 views
    This topic created in 1752 days ago, the information mentioned may be changed or developed.

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

    疑问的关键代码:

    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 结构体完全不一样,就可以实现断言?

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

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

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


    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  
       Aug 15, 2021
    @EscYezi 看了 2 楼的连接 你说的对 stdLogger 实现了 Logger 接口
    zzyphp111
        12
    zzyphp111  
    OP
       Aug 15, 2021
    @EscYezi #10 嗯嗯,看了你的,感觉非常直观,也解决了我的疑问, 只要存在这个结构体的实现 就可以断言成功。

    感谢
    kksco
        13
    kksco  
       Aug 16, 2021   ❤️ 1
    zzyphp111
        14
    zzyphp111  
    OP
       Aug 16, 2021
    @kksco #13 非常感谢
    Ansen
        15
    Ansen  
       Nov 16, 2022
    我也在这里迷糊了,搜索到这帖子了

    然后学习群的群友推荐了这篇文章,看完茅塞顿开

    https://go-kratos.dev/en/blog/
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   3529 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 39ms · UTC 10:48 · PVG 18:48 · LAX 03:48 · JFK 06:48
    ♥ Do have faith in what you're doing.