
疑问的关键代码:
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 |
15 Ansen 2022-11-16 17:26:35 +08:00 |