疑问的关键代码:
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 结构体完全不一样,就可以实现断言?
1
johnwood 2021-08-14 21:34:52 +08:00
golang 的类型系统和 java 之类的不一样,他是鸭子类型系统,结构不用明确表明自己实现了某个接口,也没有 java 那样的“继承”。go 的倾向是组合优于继承。
回到问题,估计因为 stdLogger 里有嵌入的 log *log.Logger |
2
zzyphp111 OP |
3
zzyphp111 OP @johnwood #1 这也是我所以疑问的,你看 zap 的引入就没有嵌入 log *log.Logger,但也断言成功了,这是为什么 https://github.com/go-kratos/kratos/blob/main/examples/log/zap.go
|
4
gjquoiai 2021-08-14 22:57:21 +08:00
一个编译时检查某个东西是否实现了某个接口的技巧,现在用的不多了,都用 linter 了
|
6
comwrg 2021-08-14 23:25:06 +08:00
4L 说的对,是用来编译期保证正确的实现了某个接口,而不是等着运行之后程序 panic
|
7
EscYezi 2021-08-15 00:05:03 +08:00 via iPhone
只要实现了 Logger 所有的方法就实现了 Logger 接口
楼主提到的 stdLogger 和 zapLogger 可以断言成功是因为两个结构体都实现了 Logger 接口的 Log 方法,和内嵌的那个 log 成员没关系 |
9
EscYezi 2021-08-15 00:18:07 +08:00 via iPhone
@johnwood #8 实例化的 stdLogger 啊,stdLogger 实现了 Logger 接口
|
10
EscYezi 2021-08-15 00:52:54 +08:00
可以尝试运行一下这段代码
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") } |
12
zzyphp111 OP |
13
kksco 2021-08-16 13:53:49 +08:00 1
|
15
Ansen 2022-11-16 17:26:35 +08:00
|